- каждая запись каталога задач получила тип вместо тега kind: и префикса заголовка; секция роадмапа «Разработка» стала «Сопровождением», порядок секций канонический - поправлены протухшие факты: нереализованные маршруты Read API, MCP и `healthlog import`, словарь слоёв в инварианте, семантика гейта по покрытию диффа, периметр перестал дублировать security.md - замер слияния переведён с находки 49 на находку 54, заполнены Purpose спек storage и parsing
5.6 KiB
✨ Назвать механизм отката релиза после наката миграции
- Тип: feature
- Категория: Инфра
- Зачем: Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
- Теги: goal:deploy
Решение принято владельцем 2026-08-02: вариант (2) — копия файла базы перед
накатом. Entrypoint контейнера копирует файл базы рядом до старта бинаря,
откат = подмена файла. Down-блоки миграций при этом честно называются
декорацией для локальной разработки, а не аварийным путём: ни один из них не
исполнялся ни разу. Осталось решить при взятии — сколько копий держим и где.
Задача естественно склеивается с деплоем. Ниже —
исходная постановка блокера, она же ТЗ.
Вынуто ревью кода задачи «Дозакрыть находки ревью по слиянию сущностей»
(проходы ops и negative, профиль deep).
Двигает строку «Завершения» цели: «Откат релиза после наката миграции имеет названный механизм».
Что именно решить
Та задача перенесла в store.Open стража версии схемы: база новее бинаря —
отказ на старте. Решение принято владельцем и здесь не пересматривается. Но у
него есть следствие, которое до сих пор нигде не было записано:
после того как новый бинарь накатил миграцию, возврат старого бинаря приёма не чинит. Он теперь отказывается стартовать, а понизить схему нечем:
- подкоманды миграции у бинаря нет (
serve,reindex,healthcheck); gooseCLI в образ не кладётся;- блоки
-- +goose Downв миграциях написаны, но ни один тест их не исполняет, и на рабочей базе они не выполнялись ни разу (DROP COLUMNв SQLite черезmodernc.org/sqliteне проверялся вовсе); restart: unless-stoppedпревращает отказ в цикл перезапуска, а телефон всё это время шлёт в закрытый порт и не перешлёт потом.
То есть аварийный путь придётся изобретать в момент аварии, при остановленном
приёме. Цена простоя для метрик закрывается широким и глубоким проходами
синхронизации; для stateOfMind не закрывается ничем — у него доставки HAE
единственный источник.
Варианты и цена
- Подкоманда
healthlog migrate --down-to N. Цена: новая поверхность CLI плюс тест наDownкаждой миграции (сейчас их нет, иDROP COLUMNв SQLite ведёт себя не так, как в постгресе). Зато откат становится операцией, а не импровизацией. - Копия файла базы перед накатом — entrypoint контейнера делает
cpрядом, откат = подмена файла. Цена: место (база растёт), плюс правило «сколько копий держим». Зато не требует ни кода, ни доверия кDown, а база производна от архива — потеря копии не смертельна. gooseCLI в образ. Цена: образ перестаёт быть одним статическим бинарём, появляется вторая точка, знающая про схему.- Ничего, но записать вслух: «понижение схемы не поддерживается, лечение — только выкатка вперёд». Цена: в аварии выбора нет.
Рекомендация
(2) плюс уже сделанная запись из (4). Копия файла — единственный вариант,
который не требует доверять непроверенному коду ровно в тот момент, когда
проверять некогда; а Down-блоки при этом честно называются декорацией для
локальной разработки.
Что стоит, пока решения нет
Ничего: страж работает, и это правильно. Стоит только аварийный сценарий —
он существует ровно в том виде, в каком описан выше. Строка «понижение схемы не
поддерживается» уже записана в docs/architecture.md (раздел «Деплой»).