8 lines
16 KiB
JSON
8 lines
16 KiB
JSON
{
|
|
"index": 142,
|
|
"slug": "editorial-2024-01-field-legacy-modernization",
|
|
"title": "Модернизация legacy-системы: как ограничить rollout и не перепутать rollback с восстановлением данных",
|
|
"excerpt": "Пошаговая схема замены одного участка legacy-системы: наблюдаемый симптом, decision gate, control, ограниченная волна и отдельная проверка обратимости данных.",
|
|
"contentHtml": "<p>В день переключения новый обработчик отвечает успешно, но команда не может быстро ответить на четыре вопроса: какой трафик он получил, с чем его сравнивать, кто остановит волну и что произойдёт с уже записанными данными. Обычно звучит: «включим на десять процентов, а если что — откатим». Процент не задаёт границу риска. Слово «откат» не объясняет, вернётся ли только маршрут или ещё и состояние системы.</p>\n<p>Цена ошибки — не только временная деградация. Новый путь может отправить письмо, создать платёж, изменить баланс или записать событие до того, как команда заметит проблему. Переключатель вернёт следующие запросы в старую систему, но уже созданный эффект останется. Поэтому модернизация legacy начинается с узкого шва и проверяемого решения, а не с общего обещания переписать всё.</p>\n<p><strong>Тезис.</strong> Безопасная замена legacy — это последовательность границ: один маршрут, явный владелец, сравнимый control, ограниченное окно и отдельно описанный путь возврата. Rollout отвечает на вопрос «кому разрешено увидеть новый код». Rollback-route отвечает на вопрос «куда направить следующий запрос». Восстановление данных отвечает на другой вопрос: «что делать с эффектами, которые уже произошли».</p>\n<h2>Сначала найти шов</h2>\n<p>Шов — участок поведения, который можно отделить от остальной системы. Это может быть чтение каталога, расчёт тарифа или выдача профиля. Для первого шага лучше выбрать операцию с понятным входом, ограниченным числом потребителей и наблюдаемым результатом. Если действие меняет деньги, права или внешнюю запись, его граница должна включать эти эффекты, а не только HTTP-ответ.</p>\n<p>Прокси или адаптер принимает запрос и выбирает старый либо новый обработчик. Сначала он может передавать запрос в legacy без изменения. Затем команда добавляет новый обработчик за той же границей. Такой подход оставляет старый путь доступным, пока новый контракт не проверен. Маршрут должен быть перехватываемым, а состояние — достаточно понятным для сравнения.</p>\n<table><caption>Что должно быть известно до ограниченного включения</caption><thead><tr><th>Поле</th><th>Пример</th><th>Зачем нужно</th></tr></thead><tbody><tr><td>Шов</td><td><code>GET /catalog/item</code></td><td>Ограничивает область изменения</td></tr><tr><td>Владелец</td><td>Команда каталога</td><td>Назначает решение</td></tr><tr><td>Control</td><td>Тот же запрос через legacy</td><td>Даёт точку сравнения</td></tr><tr><td>Сигнал</td><td>Код ответа и время</td><td>Задаёт наблюдаемый признак</td></tr><tr><td>Возврат</td><td>Предыдущая версия правила</td><td>Показывает обратимое действие</td></tr><tr><td>Данные</td><td>Только чтение</td><td>Отделяет маршрут от восстановления</td></tr></tbody></table>\n<p>Таблица не заменяет проверку поведения. Одинаковый статус <code>200</code> может скрывать другой набор полей, задержку или побочный эффект. Для каждого шва нужны valid, invalid и repeat-сценарии. Повтор особенно важен для операций с ключом идемпотентности: одинаковый запрос не должен создать второй эффект.</p>\n<figure><img src=\"/assets/editorial/2024/legacy-modernization-2024-rollout-gate.svg\" alt=\"Схема поэтапного rollout legacy-модернизации: legacy-only, ограниченное предложение нового пути, ручная проверка и возврат к legacy-only; граница данных показана отдельно\" loading=\"lazy\" /><figcaption>Учебная схема показывает порядок решения, а не выполненный rollout. В ней нет реальных процентов трафика, метрик или доказательства успешного возврата.</figcaption></figure>\n<h2>Механизм: gate, control и окно</h2>\n<p>Decision gate должен проверять один класс риска. Gate шва проверяет область и владельца. Gate совместимости проверяет входы, ответы и эффекты. Gate доставки проверяет версию адаптера и правило маршрутизации. Gate наблюдения проверяет control, population, duration и источник сигнала. Gate возврата проверяет только обратимое действие. Статус одного gate не доказывает остальные.</p>\n<p>Ограниченная волна имеет смысл только рядом с control. Если новый путь обслуживает пользователей без скидок, а legacy — остальных, различие может объясняться составом аудитории. Если окно короче агрегации метрики, сигнал опоздает. Если оба пути используют общий кеш или базу, новый код способен изменить поведение старого. Такое наблюдение останавливает вывод «новая версия сломана», но не отменяет расследование.</p>\n<p>Учебный пример ниже показывает форму решения для операции чтения. Имена и значения вымышлены; пример не сообщает о реальном сервисе, трафике или измерении.</p>\n<pre><code>const gate = {\n seam: 'catalog.item.read',\n owner: 'catalog-team',\n population: 'tenant=demo',\n control: 'legacy-v3',\n candidate: 'adapter-v1',\n duration: '15m',\n signal: ['status_code', 'latency_ms', 'schema_diff'],\n decision: 'manual_review',\n rollbackRoute: 'route -> legacy-v3',\n dataEffect: 'read-only',\n};\n\nif (gate.dataEffect !== 'read-only' && !reconciliationOwner) {\n throw new Error('data boundary is unknown');\n}</code></pre>\n<p>Последняя проверка намеренно блокирует операцию. Если новый путь пишет данные, одного правила маршрута недостаточно. Нужны идентификатор эффекта, журнал, владелец сверки и решение для повторного запроса. Когда условия неизвестны, безопасный результат — не расширять волну и оставить шов на legacy.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><caption>Диагностика готовности к замене</caption><thead><tr><th>Симптом</th><th>Причина</th><th>Проверка</th><th>Действие</th></tr></thead><tbody><tr><td>Обсуждают только процент</td><td>Процент приняли за стратегию</td><td>Назвать population, control, duration и signal</td><td>Остановить включение</td></tr><tr><td>Оба пути вернули <code>200</code></td><td>Сравнили транспорт, не поведение</td><td>Проверить поля, ошибки, время и эффект</td><td>Добавить case и владельца</td></tr><tr><td>«Откат» означает выключение флага</td><td>Маршрут смешали с данными</td><td>Перечислить записи и внешние вызовы</td><td>Описать reconciliation или запретить запись</td></tr><tr><td>Сигнал нового пути хуже control</td><td>Различается population или shared state</td><td>Сопоставить запросы, окно и зависимости</td><td>Поставить волну на паузу</td></tr><tr><td>Неизвестно, кто вернёт маршрут</td><td>Нет владельца и prior state</td><td>Проверить возврат без пользовательского трафика</td><td>Не выдавать разрешение</td></tr></tbody></table>\n<h2>Порядок действий</h2>\n<ol><li>Выберите один шов и запишите владельца, потребителей и границу состояния.</li><li>Опишите legacy-контракт: успешный вход, отказ, повтор и побочный эффект.</li><li>Поставьте адаптер перед старым обработчиком и проверьте исходный маршрут.</li><li>Добавьте новый обработчик за той же границей. Начните с чтения или однозначно сверяемого эффекта.</li><li>Определите control, population, duration, сигналы и источник каждого сигнала.</li><li>Проверьте valid, invalid и repeat на одинаковых входах.</li><li>Проведите ограниченную волну с ручным решением: расширить, остановить или вернуть маршрут.</li><li>Отдельно подтвердите возврат маршрута и состояние данных.</li><li>Расширяйте область только после разбора существенных расхождений. Unknown оставляйте на legacy.</li></ol>\n<h2>Почему rollback не исправляет данные</h2>\n<p>Для read-only шва возврат маршрута обычно проще: следующий запрос снова идёт в известную версию. Но общий кеш, sticky session или изменённая схема могут связать пути. Нужно проверить, что старый обработчик принимает текущее состояние и что новый код не изменил его косвенно.</p>\n<p>Для write-операции новый обработчик мог создать заказ, отправить сообщение, вызвать платёжный шлюз или записать событие. Отключение адаптера остановит новые вызовы, но не отменит внешний эффект. Компенсация может быть невозможна, дублировать действие или потребовать бизнес-решения. Перед такой миграцией нужны record id, журнал состояния, владелец сверки и ответ для повторной доставки.</p>\n<p>Если команда не может назвать эти элементы, не пишите аварийный delete-скрипт. Сузьте первый шов до чтения, добавьте preview или оставьте запись в legacy. Отложенное изменение сохраняет управляемость. Быстрое переключение без границы данных переносит проблему в момент, когда исправление дороже.</p>\n<h2>Ограничения и критерий готовности</h2>\n<p>Canary не заменяет тесты. Небольшая доля трафика снижает область воздействия, но не доказывает полноту поведения. Synthetic нагрузка не показывает все состояния реальных пользователей. Общие базы, кеши, очереди и внешние провайдеры могут испортить независимость control и нового пути. Автоматический rollback полезен только там, где действие действительно обратимо.</p>\n<p>Универсального безопасного процента нет. Для системы, где каждый запрос меняет баланс, десять процентов могут быть слишком много. Для чтения сто процентов допустимы после проверки совместимости. Число выбирают после определения population, эффекта и времени обнаружения проблемы.</p>\n<p><strong>Критерий готовности проверяем.</strong> Другой инженер без устного контекста может показать владельца; legacy и candidate версии; control; population и duration; сигналы и их источники; valid, invalid и repeat cases; действие возврата; список необратимых эффектов и владельца сверки. Команда может выполнить безопасное обратное переключение на тестовом контуре и увидеть, куда пойдут следующие запросы. Если пункт неизвестен, готовность не доказана и волну не расширяют.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/strangler-fig.html\" target=\"_blank\" rel=\"noopener\">AWS Prescriptive Guidance: Strangler Fig pattern</a> — поэтапная замена функций через прокси и маршрутизацию.</li><li><a href=\"https://sre.google/workbook/canarying-releases/\" target=\"_blank\" rel=\"noopener\">Google SRE Workbook: Canarying Releases</a> — control, ограниченное по времени включение и сравнение сигналов.</li><li><a href=\"https://sre.google/workbook/on-call/\" target=\"_blank\" rel=\"noopener\">Google SRE Workbook: On-Call</a> — различие между rollback и последствиями повреждения данных.</li></ul>"
|
|
}
|