Files
healthlog/docs/backlog/otkat-reliza-posle-migracii.md
T
av 98e0772ec5 беклог: оба блокера закрыты решениями владельца
- порядок журнала при конкурентных приёмах: повторы, но после /stats;
  до тех пор живём с записанным в спеке пределом
- откат релиза после наката миграции: копия файла базы перед накатом,
  Down-блоки честно названы декорацией для локальной разработки
2026-08-02 17:10:19 +03:00

5.1 KiB

Чем откатывать релиз после наката миграции

Приоритет: средний

Решение принято владельцем 2026-08-02: вариант (2) — копия файла базы перед накатом. Entrypoint контейнера копирует файл базы рядом до старта бинаря, откат = подмена файла. Down-блоки миграций при этом честно называются декорацией для локальной разработки, а не аварийным путём: ни один из них не исполнялся ни разу. Осталось решить при взятии — сколько копий держим и где. Задача естественно склеивается с деплоем. Ниже — исходная постановка блокера, она же ТЗ.

Вынуто ревью кода задачи «Дозакрыть находки ревью по слиянию сущностей» (проходы ops и negative, профиль deep).

Что именно решить

Та задача перенесла в store.Open стража версии схемы: база новее бинаря — отказ на старте. Решение принято владельцем и здесь не пересматривается. Но у него есть следствие, которое до сих пор нигде не было записано:

после того как новый бинарь накатил миграцию, возврат старого бинаря приёма не чинит. Он теперь отказывается стартовать, а понизить схему нечем:

  • подкоманды миграции у бинаря нет (serve, reindex, healthcheck);
  • goose CLI в образ не кладётся;
  • блоки -- +goose Down в миграциях написаны, но ни один тест их не исполняет, и на рабочей базе они не выполнялись ни разу (DROP COLUMN в SQLite через modernc.org/sqlite не проверялся вовсе);
  • restart: unless-stopped превращает отказ в цикл перезапуска, а телефон всё это время шлёт в закрытый порт и не перешлёт потом.

То есть аварийный путь придётся изобретать в момент аварии, при остановленном приёме. Цена простоя для метрик закрывается широким и глубоким проходами синхронизации; для stateOfMind не закрывается ничем — у него доставки HAE единственный источник.

Варианты и цена

  1. Подкоманда healthlog migrate --down-to N. Цена: новая поверхность CLI плюс тест на Down каждой миграции (сейчас их нет, и DROP COLUMN в SQLite ведёт себя не так, как в постгресе). Зато откат становится операцией, а не импровизацией.
  2. Копия файла базы перед накатом — entrypoint контейнера делает cp рядом, откат = подмена файла. Цена: место (база растёт), плюс правило «сколько копий держим». Зато не требует ни кода, ни доверия к Down, а база производна от архива — потеря копии не смертельна.
  3. goose CLI в образ. Цена: образ перестаёт быть одним статическим бинарём, появляется вторая точка, знающая про схему.
  4. Ничего, но записать вслух: «понижение схемы не поддерживается, лечение — только выкатка вперёд». Цена: в аварии выбора нет.

Рекомендация

(2) плюс уже сделанная запись из (4). Копия файла — единственный вариант, который не требует доверять непроверенному коду ровно в тот момент, когда проверять некогда; а Down-блоки при этом честно называются декорацией для локальной разработки.

Что стоит, пока решения нет

Ничего: страж работает, и это правильно. Стоит только аварийный сценарий — он существует ровно в том виде, в каком описан выше. Строка «понижение схемы не поддерживается» уже записана в docs/architecture.md (раздел «Деплой»).