D-08 · Миграция данных без остановки#
- Версия: 2.1 · 2026-09-21 · по мастер-шаблону v6.0
- Известно также как: переезд данных, live migration, перенос в другое хранилище, backfill, «как переехать и не уронить»
- Этап: W3 · Данные: модель и хранилище — но применяется после того, как решение о переезде принято; окно сосуществования проверяется на W10
- Стадия: под изменением — предмет и есть переход: на гринфилде переходить не от чего
- Профиль: дорого изменить · высокое влияние → полная форма
- Не решает: куда переезжать — это D-01; надо ли делить данные и по какой линии — D-07; как выкатывать код, который с этими данными работает, — D-09
- Не путать с: D-07 — там решают, делить ли, здесь — как перебраться, когда решение уже принято
- Карточки: DATA-17 миграции схемы без простоя · DATA-16 целостность и сверка данных · DATA-09 репликация · MSG-07 outbox и CDC · ARCH-13 strangler fig
Первый документ третьей стадии — «системы под изменением». Прежние семь решают, какой системе быть. Этот — как перейти от одной к другой, не останавливаясь. Пласт был пуст: оба прогона кончились миграцией и оба записали её в последствия чужих ADR, а не в решения.
Почему он первым. Данные говорят, что изменение системы ломает её чаще, чем её собственный код: в выборке инцидентов облачных сервисов конфигурация даёт 24,5 % причин, операционные ошибки 12,7 %, а ошибки в коде — 21,5 % (arXiv:2504.08865).
Область решения — один переезд одного набора данных. «Миграция в системе» — не решение.
1. Что выяснить#
| Вопрос | Где смотреть | Заполняет |
|---|---|---|
| Что переезжает: объём, число сущностей, темп записи? | Схема, метрики | Длительность переноса, выбор формы |
| Все ли писатели этих данных известны? | Права доступа, код, задания по расписанию, ручные правки | Дисквалификатор |
| Чем будем сверять источник и приёмник — и сколько это стоит? | Возможности обоих хранилищ | Дисквалификатор и половина решения |
| Сколько можно быть недоступным — и можно ли вообще? | Требования W0, продукт | Отсекает или разрешает остановку на окно |
| Что произойдёт с данными, записанными во время переезда? | Темп записи, способ догона | Форма переезда |
| Кто читает эти данные и умеет ли читать из обоих мест? | Потребители, включая отчётность и выгрузки | Порядок переключения |
| Где точка невозврата и сколько времени до неё? | Сам план переезда | Обратимость на каждом шаге |
Сверка — не этап миграции, а условие её обратимости#
Третий вопрос решает больше остальных. Пока нет способа сравнить перевезённое с исходным, откатиться нельзя — не потому, что технически сложно, а потому, что неизвестно, какая сторона права. Миграция без сверки необратима с первого шага, как бы аккуратно ни был расписан план.
Сверка бывает трёх сортов, и стоят они очень по-разному:
| Сорт | Что делает | Когда достаточно |
|---|---|---|
| Счётная | Сравнивает количества по срезам | Первый фильтр; ловит потерю, не ловит порчу |
| Выборочная | Сравнивает случайные записи целиком | Постоянный режим во время переезда |
| Сплошная | Сравнивает всё | Перед точкой невозврата, хотя бы раз |
Если сплошная невозможна по объёму — это факт, который меняет решение, а не деталь исполнения: он поднимает цену ошибки и обычно заставляет выбрать форму с переключением по частям.
Про второй вопрос#
Незнакомый писатель — самая дешёвая причина провалить миграцию. Задание по расписанию, о котором помнил уволившийся, административная панель, скрипт поддержки. Приёмник разойдётся с источником молча: ошибок не будет, расхождение обнаружится месяцами позже. Список писателей собирается по правам доступа, а не по памяти команды.
2. Оси и дисквалификаторы#
Оси сравнения. Полнота сверки · обратимость на каждом шаге · окно недоступности · нагрузка на источник во время переезда · стоимость сосуществования · длительность.
Ось обратимости здесь главная, а не одна из. В остальных решениях обратимость — довод при прочих равных; здесь она определяет, что произойдёт в худший день. Форма, у которой откат есть только до первого шага, и форма, где откат есть на каждой когорте, различаются не удобством, а тем, чем кончится ошибка.
И у неё два значения, которые легко принять за одно.
| Вид обратимости | Что возвращает | Чем обеспечивается |
|---|---|---|
| Операционная | Возможность остановиться: не переносить дальше, вернуть трафик на источник | Когорты, переключение чтения раньше записи, флаги |
| Информационная | Состояние мира: сделанного как не бывало | Тем, что приёмник наш и мы можем из него удалить |
Пока приёмник наш, они совпадают, и различать их незачем. Как только приёмник чужой — другая организация, партнёр, покупатель части бизнеса, — они расходятся полностью: шаг остаётся обратимым операционно и становится необратимым информационно. Переданные данные не отзываются ни остановкой, ни откатом, ни договором.
Практическое следствие одно и решающее: точка невозврата переезжает. Она оказывается не на переключении и не на договорной дате, а на первой переданной порции. На прогоне U-01 это выяснилось седьмым вопросом раздела 1 — ось его не задавала.
Заполняя ячейку обратимости, пишите оба значения или одно с оговоркой «приёмник наш». Ячейка «откат на каждой когорте» без этой оговорки читается как информационная обратимость и ею не является.
Дисквалификаторы.
- Сверки нет. Нельзя убедиться, что перевезённое совпадает с исходным, — миграция необратима с первого шага. Отсекается не форма, а готовность начинать.
- Писатели известны не все. Хотя бы один неучтённый — и приёмник расходится молча.
- Переключение без подтверждения, что приёмник догнал. Сорок три секунды сетевого разделения дали сутки деградации именно потому, что «базы в обоих центрах содержали записи, отсутствующие в другом», и обратное переключение стало невозможным (GitHub, 2018). Подтверждение догона — не проверка перед переключением, а его предусловие.
- Двойная запись без единого источника порядка. Тот же дисквалификатор, что в D-07: гонка и частичный отказ дают расхождение, невидимое до сверки. Во время миграции двойная запись часто неизбежна — тем более она обязана идти через журнал или захват изменений, а не через два вызова подряд.
- Не назначены срок и владелец снятия старого пути. Временное состояние становится постоянным, когда его снятие никому не поручено. Здесь это дороже обычного: сосуществование двух хранилищ означает двойную стоимость и двойную правку на каждой задаче.
Первые два отсекают начало, третий и четвёртый — способ, пятый добавляет работу.
3. Матрица оценки#
Колонки — формы переезда из раздела 4, применимые к этому набору данных. В ячейках факты с пометкой источника.
Словарь пометок. [схема] · [замер] · [расчёт] · [требования] — из обязательств W0 · [проба] — результат пробного переноса на копии или на части данных · [нет данных].
Пометка [проба] — местная и важная. Единственное решение в наборе, где дешёвый эксперимент доступен почти всегда: перенести один процент и сверить. Оценка длительности и полноты сверки, полученная пробой, отличается от расчётной систематически, а не случайно.
Пример условный. Числа выдуманы, форма настоящая.
| Ось | Что записывать | Остановка на окно | Копия плюс догон | По ключу, когортами |
|---|---|---|---|---|
| Полнота сверки | Какой сорт возможен и сколько стоит | Сплошная до включения [проба] |
Выборочная постоянно, сплошная перед переключением [расчёт] |
Сплошная на каждой когорте [расчёт] |
| Обратимость | Что можно вернуть и на каком шаге | Только до начала окна | До переключения; после — только вперёд | На каждой когорте |
| Окно недоступности | Минуты | 40 мин [проба] |
0 | 0 |
| Нагрузка на источник | Прирост при переносе | Нет — система стоит | +30 % чтений на время переноса [замер] |
+5 % на когорту [расчёт] |
| Стоимость сосуществования | Что платим, пока живут оба | Нет | Два хранилища, двойная запись | Два хранилища дольше всех |
| Длительность | От начала до снятия старого | 40 мин | 2 недели [расчёт] |
2 месяца [расчёт] |
Нулевой вариант — «не переезжать». Он законен и здесь чаще, чем кажется: стоимость переезда с сосуществованием, двойной записью и сверкой иногда превышает то, ради чего он затевался. Считать его надо явно.
Строка окна недоступности — единственная, где остановка выигрывает вчистую, и это стоит помнить: если окно есть, простой честный переезд дешевле любой изощрённой схемы и обратим целиком.
4. Типовые формы переезда#
Рынка здесь нет. Формы не устаревают, даты съёмки у раздела нет.
| Форма | Как устроена | Когда верна | Чем оборачивается при ошибке | Статус у нас |
|---|---|---|---|---|
| Не переезжать | Нулевой вариант | Выигрыш меньше стоимости сосуществования | Проблема, ради которой затевались, остаётся — но осознанно | заполняется в проекте |
| Остановка на окно | Останавливаем запись, переносим, включаем | Окно есть и укладывается в обещание | Окно оказалось длиннее расчёта, а назад уже нельзя — отсюда обязательная проба | |
| Копия плюс догон | Снимок, затем догон изменений из журнала, затем переключение | Объём велик, остановка недопустима | Догон не успевает за темпом записи: переключения не будет никогда | |
| Двойная запись с фоновым переносом | Пишем в оба, старое переносим фоном | Нет журнала изменений, но есть контроль над всеми писателями | Расхождение, невидимое без сверки; поэтому только через единый источник порядка | |
| Чтение из обоих, запись в новое | Читаем сначала из нового, при промахе из старого и дописываем | Данные читаются неравномерно, «холодные» можно не переносить | Холодный хвост не переезжает никогда, и старое хранилище не выключить | |
| По ключу, когортами | Арендаторы, диапазоны, регионы — по одной группе | Ключ естественный и данные по нему не пересекаются | Запрос, пересекающий когорты, ломается на время переезда | |
| Strangler: новое — в новое | Новые данные пишем в приёмник, старые остаются где были | Исторические данные читаются редко и отдельно | Два места навсегда; граница «новое/старое» становится вечной | |
| Двойное чтение со сравнением | Читаем из обоих и сравниваем, не переключаясь | Не перенос, а проверка перед ним | Принимают за миграцию; сам по себе он ничего не переносит |
Формы комбинируются, и обычно правильно именно это. Типичная связка: двойное чтение со сравнением как проверка → копия плюс догон как перенос → по ключу как порядок переключения.
Источники раздела: Kleppmann — логи и почему двойная запись плоха · GitHub — разбор инцидента 21 октября 2018 · каталог практик — strangler и anti-corruption layer · реестр исследования
5. Эвристики#
Сверку проектируют раньше способа переноса. Не «перевезём и проверим», а «вот чем проверим — теперь выбираем, как везти». Форма, под которую нет сверки, отпадает независимо от своих достоинств.
Чтение переключают раньше записи. Переключить чтение обратимо: не понравилось — вернули. Переключить запись — почти нет: в приёмнике появляются данные, которых нет в источнике, и обратный ход становится слиянием, а не переключением.
Двойная запись — режим, а не приём. У неё обязаны быть единый источник порядка, сверка и срок. Без срока это не миграция, а новая постоянная архитектура, выбранная случайно.
Точка невозврата называется до начала. Один вопрос: после какого шага вернуться дороже, чем закончить? Ответ пишется в ADR. Команда, которая не знает ответа, узнаёт его в неподходящий момент.
Когорты дают откат, которого не даёт ничто другое — пока приёмник наш. Даже когда естественного ключа нет, его иногда стоит выдумать: диапазон идентификаторов не хуже арендатора. При чужом приёмнике когорты дают только право остановиться, и это стоит написать в ADR словами, а не подразумевать: команда, прочитавшая «откат на каждой когорте», будет считать, что вернуть можно всё.
Проба на одном проценте заменяет любую оценку длительности. Это единственное решение в наборе, где эксперимент дешевле рассуждения; пометка [проба] для того и заведена.
«Нет данных» здесь почти не бывает. Объём, темп записи, число писателей и стоимость сверки считаются по схеме, правам и метрикам за часы. Пустая ячейка обычно означает не «не померили», а «не нашли всех писателей» — то есть несработавший дисквалификатор.
6. Запись в ADR#
| Поле ADR | Чем заполняется |
|---|---|
| Контекст | Что переезжает, откуда, куда, зачем; полный список писателей; объём и темп записи |
| Дисквалификаторы | Какие формы отсечены; отдельно — что превратилось в работу до начала |
| Рассмотренные варианты | Матрица раздела 3 снимком, включая нулевой вариант и результаты пробы |
| Решение | Форма, порядок шагов, точка невозврата, порядок переключения чтения и записи |
| Обоснование | Почему эта форма при этой сверке; что показала проба |
| Последствия | Сверка как постоянный механизм; стоимость сосуществования; что делать, если догон не успевает |
| Условия пересмотра | Раздел 7 |
| Метод | D-08 v<версия> · шаблон ADR v<версия> — рыночного снимка нет |
Три строки обязательны, и без них запись считается неполной: способ сверки · точка невозврата · срок сосуществования и имя того, кто снимает старый путь.
Четвёртая обязательна, когда приёмник не наш: чем обратимость является на каждом шаге — операционной, информационной или обеими.
Связанное решение. Переезд данных почти всегда идёт вместе с выкатом кода, который с этими данными работает: у них общее окно и общая дата, а точка невозврата и откат общие только пока приёмник наш: при чужом приёмнике точка переезжает на первую переданную порцию (раздел 2), а у кода остаётся на переключении. Записываются они одним ADR — общий контекст, отдельные короткие записи, общие последствия (мастер-шаблон, раздел о связанных решениях).
7. Условия пересмотра#
- Догон перестал успевать за темпом записи — переключения не будет, форму надо менять на ходу.
- Сверка показала расхождение, которого не объясняет ни один известный писатель: список писателей неполон.
- Точка невозврата пройдена, а условия изменились — это уже не пересмотр решения, а новое решение о том, что делать дальше.
- Срок сосуществования истёк, старый путь не снят.
- Объём или темп записи выросли настолько, что проба перестала быть представительной.
- Появился потребитель, читающий данные напрямую, — его не было в списке.
Проверка перед тем, как считать решение принятым — пять пунктов в шаблоне. Шестой, местный: проба проведена и её результат стоит в матрице. Миграция — единственное решение в наборе, где оценку можно заменить измерением до начала работ, и не воспользоваться этим значит выбирать вслепую там, где видно.
Источники: Kleppmann — Using logs to build a solid data infrastructure · GitHub — October 21 post-incident analysis · Yan, Chen, Ma и др. — An Empirical Study of Production Incidents, arXiv:2504.08865 · microservices.io — pattern language · 00-workflow.md, шаг W3
История версий#
| Версия | Дата | Изменение |
|---|---|---|
| 2.1 | 2026-09-21 | MINOR, по итогам вычитки Ш1. Раздел 6 больше не называет точку невозврата и откат безусловно общими с D-09 — ровно то, что раздел 2 говорит с версии 2.0, и то, что документ до сих пор опровергал сам себе внутри одного файла. Старым ADR: единственный — ADR-0002 прогона U-01, и его строка «общее у двух решений: окно, дата и точка невозврата» опровергнута находкой 2 того же прогона; приёмник там чужой. Принятый ADR не правится — читать его надо вместе с находками |
| 2.0 | 2026-09-18 | MAJOR, по итогам узкого прогона U-01 — первого применения этого документа. Ось обратимости разделена на операционную и информационную: пока приёмник наш, они совпадают; при чужом приёмнике расходятся полностью, и точка невозврата переезжает на первую переданную порцию. Поправлена эвристика о когортах, добавлена четвёртая обязательная строка записи. Старым ADR: он один — ADR-0002 того же прогона, написанный по v1.0. Переделывать его не нужно: различение операционной и информационной обратимости он уже проводит — в разделе «Замечание: ось обратимости не различает двух вещей», где оно и было найдено. Перепроверить в нём одно: четвёртой строки записи — чем обратимость является на каждом шаге — в его «Решении» нет, она осталась в замечании. ADR по прежним версиям намеренно не переписываются. (Строка переписана 2026-09-21 по развилке Ж3: прежняя редакция говорила «перепроверять нечего, документ не применялся ни разу» — буквально верно и на вопрос не отвечает.) |
| 1.0 | 2026-09-17 | Первая версия и первый документ третьей стадии. Собран из записи P-CHG-01 реестра исследования, поднятой на первое место данными о причинах инцидентов. 7 вопросов, 6 осей с обратимостью в качестве главной, 5 дисквалификаторов, 8 типовых форм переезда вместо рыночного слоя, местная пометка [проба], шестой пункт проверки — проведённая проба |