Files
healthlog/docs/tasks/items/release-rollback-after-migration.md
av 3d24248075 docs: документация приведена к канону av-dev-pm 4
- каждая запись каталога задач получила тип вместо тега kind: и префикса
  заголовка; секция роадмапа «Разработка» стала «Сопровождением», порядок
  секций канонический
- поправлены протухшие факты: нереализованные маршруты Read API, MCP и
  `healthlog import`, словарь слоёв в инварианте, семантика гейта по покрытию
  диффа, периметр перестал дублировать security.md
- замер слияния переведён с находки 49 на находку 54, заполнены Purpose спек
  storage и parsing
2026-08-05 19:09:35 +03:00

5.6 KiB

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

  • Тип: feature
  • Категория: Инфра
  • Зачем: Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
  • Теги: goal:deploy

Решение принято владельцем 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 (раздел «Деплой»).