Миграция может изменить миллионы записей, повлиять на работающие процессы и вызвать эффекты за пределами базы данных. Если преобразование завершается с ошибкой на полпути, восстановить полную копию не всегда безопасно или приемлемо: после создания копии могли быть записаны новые данные, а система может не выдержать простоя, необходимого для восстановления.
Поэтому план отката миграции данных не должен сводиться к команде отмены изменений. В нём нужно определить, как выявить затронутое состояние, какие операции можно отменить, какие требуют компенсации и когда целесообразно исправить ситуацию или возобновить выполнение. Решение следует подготовить до запуска миграции, установив критерии, которые команда сможет проверить в стрессовой ситуации.
Почему восстановление копии не всегда позволяет откатить изменения

Резервная копия помогает восстановить данные при определённых инцидентах, однако её восстановление может удалить правомерные изменения, внесённые после её создания. Оно также может привести к недоступности системы, повторному построению индексов или потере записей, уже переданных в другие системы. Если используются репликация, очереди, экспорты или интеграции, восстановление базы данных автоматически не отменит эти эффекты.
Важно различать три действия. Восстановление возвращает систему к состоянию резервной копии или определённого момента времени; откат пытается отменить изменения миграции; компенсация применяет новые операции, корректирующие её последствия. Это не одно и то же: после компенсации история может отличаться от исходной, даже если бизнес-правила снова соблюдаются.
Выбор зависит от масштаба сбоя, последующих записей и целевых показателей восстановления. До начала определите, какая потеря данных допустима, как долго может длиться перерыв и кто уполномочен разрешить восстановление. Если эти ограничения не заданы, у команды нет операционных критериев для принятия решения.
Классифицируйте каждое преобразование по возможности восстановления
Опишите каждый шаг миграции и классифицируйте его в соответствии с предусмотренным способом восстановления:
- Обратимое: существует надёжная обратная операция. Например, исходное значение сохраняется до нормализации поля и может быть восстановлено без перезаписи последующих изменений.
- Компенсируемое: точно восстановить предыдущее состояние невозможно, но новая операция может исправить последствия в соответствии с бизнес-правилом. Компенсация должна быть явной, проверяемой и, по возможности, идемпотентной.
- Необратимое: данные удаляются или возникает эффект, который нельзя гарантированно отменить. Необходимо явно принять решение о допустимости риска, сохранении исходных данных и дополнительных проверках.
Не следует присваивать категорию только по типу SQL-оператора. Массовое обновление может быть обратимым, если сохранить прежние значения и контролировать конкурентный доступ; но оно может стать необратимым, если во время выполнения другие процессы изменяют те же строки. Учитывайте и побочные эффекты: уведомления, вызовы API, списания средств или сообщения в очередях. Часто такие действия лучше отделить от преобразования данных.
Зафиксируйте исходное состояние и инварианты
До запуска зафиксируйте область действия миграции: включённые сущности, фильтры, версию приложения и применяемые правила. Определите базовые значения: важные количества записей, а при необходимости — агрегаты или отпечатки стабильных наборов данных. Укажите время, к которому относятся эти значения, и их источник. Число без указания области действия и контекста не позволяет проверить результат восстановления.
Инварианты — это условия, которые должны оставаться истинными как во время миграции, так и после неё. К ним могут относиться связи между таблицами, уникальность, допустимые состояния, суммы, которые необходимо сохранить, или соответствие записей базы данных данным в подключённых системах. Для каждого инварианта добавьте воспроизводимый запрос или процедуру и критерий приемлемого результата. Если данные могут правомерно изменяться во время выполнения, определите, как отличать такую активность от последствий миграции.
В приложении на PHP преобразования можно реализовать в консольных командах или фоновых задачах, а не выполнять в длительном веб-запросе. Это не устраняет риски конкурентного доступа и ограничения транзакций: определите, какую единицу работы можно выполнить атомарно и что делать, если процесс завершится между двумя операциями.
Спроектируйте пакеты, контрольные точки и возобновляемое выполнение
Разбейте работу на пакеты с чётко заданными границами, например по стабильному упорядоченному ключу. Не используйте пагинацию по смещению, если строки могут изменяться или исчезать во время процесса: маркер продолжения на основе ключа обычно предсказуемее. Размер пакета должен обеспечивать баланс между длительностью транзакций, нагрузкой на базу данных и удобством выявления сбоев.
После каждого пакета сохраняйте контрольную точку с идентификатором задачи, обработанным диапазоном, состоянием, временем и результатами проверки. Обновление данных и продвижение контрольной точки должны быть скоординированы, чтобы пакет не считался обработанным до подтверждения изменений. Если обе операции нельзя объединить в одну транзакцию, предусмотрите механизм сверки, выявляющий промежуточное состояние.
Возобновляемая миграция не должна вслепую применять изменения повторно. Каждая операция должна выдерживать повторные попытки или проверять, не был ли её результат уже достигнут. В PHP для этого можно использовать транзакции, ограничения уникальности и идемпотентные операции — в зависимости от СУБД и модели данных. Также намеренно проверьте прерывания: развёртывание, исключение или потеря соединения не должны оставлять работу без известного способа продолжить её.
Регистрируйте изменения, чтобы находить их последствия и проверять принятые решения
Назначайте каждой попытке выполнения уникальный идентификатор и как минимум фиксируйте преобразование, область действия, пакеты, затронутые строки, ошибки и решения по восстановлению. Для изменений, которые можно компенсировать, сохраняйте необходимые исходные данные или безопасную ссылку на сохранённые исходные данные. Не записывайте в журналы конфиденциальную информацию без разбора: ограничьте доступ, срок хранения и состав данных необходимым для восстановления и аудита.
Журнал должен помогать отвечать на конкретные вопросы: какие строки пытались обработать, для каких изменения были подтверждены, где возникли сбои и какие последующие операции изменили эти строки. При необходимости сопоставляйте технические журналы с историей бизнес-изменений. Не путайте отслеживание изменений с резервным копированием: журнал должен содержать достаточно данных для своей цели, и его также необходимо защищать от утраты или подмены.
Выберите: возобновить, компенсировать или восстановить
Заранее задайте сигналы и действия в ответ на них, вместо того чтобы полагаться только на интуицию при возникновении ошибки:
- Возобновить: если сбой временный, инварианты соблюдаются, а подтверждённые пакеты известны. Повторяйте попытки в установленных пределах и следите за ошибками и нагрузкой.
- Компенсировать: если применённые изменения известны и проверена корректирующая операция. Сначала остановите новые несовместимые записи и убедитесь, что компенсация не перезапишет правомерные изменения.
- Восстановить: если повреждения обширны, восстановление из копии проверено, а последствия потери или восстановления последующих изменений приемлемы. Согласуйте восстановление с репликами и интеграциями.
- Остановить и эскалировать: если состояние невозможно определить, расхождения в подсчётах не объяснены или компенсация может усугубить ущерб. До вмешательства сохраните свидетельства.
Установите пороговые значения для приостановки работы, например частоту ошибок выше допустимой, нарушение инварианта или отклонение в подсчётах. Определите, кто вправе разрешить возобновление, а кто принимает решение о восстановлении. Иногда безопаснее всего изолировать затронутый поток и поддерживать систему в контролируемом состоянии на время расследования.
Проверьте результат и завершите миграцию по контрольному списку

Завершение процесса не доказывает корректность данных. Сопоставьте количество записей до и после миграции с ожидаемым охватом, проверьте бизнес-правила, связи и крайние значения. Используйте выборочную проверку для анализа отдельных случаев, но не вместо полной проверки, если её можно выполнить. Если есть внешние потребители данных, проверьте и их состояние, а также согласуйте порядок устранения расхождений.
Перед запуском: классифицируйте преобразования, подтвердите наличие резервной копии и возможность восстановления, проверьте пакеты и повторные попытки на репрезентативных данных, определите инварианты, условия остановки, ответственных и окно выполнения. Убедитесь, что команда может просматривать журнал изменений, а процедуры компенсации или восстановления проверены.
После запуска: проверьте количества записей и бизнес-правила, изучите ошибки и внешние эффекты, сохраните журнал выполнения и задокументируйте все исключения. Храните информацию, необходимую для восстановления, в течение согласованного периода, а затем безопасно удалите её, когда она перестанет быть нужна. Миграция считается завершённой только тогда, когда результаты можно проверить и по оставшимся отклонениям принято явное решение.



