[infra][db] Инцидент #361 повторится: мигратор падает на миграциях «задним числом» из долгоживущих веток — CI-гейт порядка + allowUnorderedMigrations; провери… #363
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Суть
Инцидент #361 (crash-loop + 502 на ~11 минут после деплоя develop-билда 2026-07-04) — не разовая ошибка, а класс отказа, который гарантированно повторится. Механика: PR #234 жил в ветке долго и принёс миграцию
20260627T130000-ai-chat-runsс датой в имени старше уже применённых на проде миграций (например,20260704T120000-client-metricsиз #355). Kysely-мигратор с дефолтными настройками требует, чтобы применённые миграции были префиксом отсортированного списка → «corrupted migrations» → приложение не стартует → crash-loop.Проверено по коду: оба мигратора работают с дефолтами,
allowUnorderedMigrationsне включён:Непосредственная угроза: открытые долгоживущие PR #119 (git-sync, живёт с июня) и #120 (offline-sync). Если в них есть миграции с июньскими датами в именах — их мерж уронит прод точно так же, как #234.
Решение (два уровня, оба)
1. CI-гейт порядка миграций (основная защита, предотвращает)
Скрипт в существующий CI-пайплайн PR: сравнить имена файлов в
apps/server/src/database/migrations/между веткой PR и целевой веткой; добавленные файлы обязаны быть лексикографически новее самого нового файла в целевой ветке:Правило для долгоживущих веток становится механическим: перед мержем переименовать миграцию на текущий таймстамп (содержимое не трогать). Гейт делает забывание невозможным.
2.
allowUnorderedMigrations: true(страховка, смягчает)Включить в обоих местах создания
Migrator. Kysely тогда применяет «пропущенные» старые миграции вместо отказа — прод не падает, даже если гейт обойдён (ручной пуш, hotfix-ветка). Трейд-офф, который надо зафиксировать в комментарии к коду: порядок применения на разных инстансах может отличаться от лексикографического, поэтому миграции должны оставаться независимыми (наши и так таковы: каждая создаёт свои объекты); гейт из п. 1 остаётся основной линией обороны, страховка — на случай его обхода.Немедленное действие (до/вне этого тикета)
Проверить содержимое
#119и#120на миграции со старыми датами:Если есть — переименовать на актуальный таймстамп в ветке до мержа (это правка имени файла, содержимое и ревью не затрагивает).
Крайние случаи
origin/${TARGET_BRANCH}на момент CI-рана; гонка двух одновременных PR с миграциями остаётся возможной (оба новее develop, но один старше другого) — её закрывает страховка п. 2.migrate down) с unordered-миграциями: down-путь у нас фактически не используется в проде; отметить в доке, что порядок down при unordered не гарантирован.Тесты / проверка
allowUnorderedMigrationsон применяется, без — падает (документирующий тест текущего поведения).Вне скоупа
План работ
allowUnorderedMigrationsв оба Migrator + комментарий с трейд-оффом + документирующий тест.