- Правило покрытия получило второй разряд (условный, как у точек), запрет вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и ряд из null больше не затирают маршрут. Победитель внутри доставки стал функцией множества версий — общим помощником с точками, — а провенанс поднимается и при совпавшем хеше, иначе отложенная доставка возвращала витрину к прежнему содержимому. - Одно поле не того типа больше не уносит сущность, а пропуски видны в учётной записи доставки (миграция 00008, NULL = «не измерялось»); каноническая форма считается один раз и вне транзакции; откат бинаря поверх новой схемы отказывает на старте; текст ошибки разбора не несёт значений из тела. - Ревью кода профилем deep (девять проходов) нашло две регрессии и обе закрыты: безусловный второй разряд запирал законный досчёт навсегда, а выбор победителя был квадратичен по числу присланных версий одного ключа.
4.3 KiB
Чем откатывать релиз после наката миграции
Приоритет: блокеры
Вынуто ревью кода задачи «Дозакрыть находки ревью по слиянию сущностей»
(проходы 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 (раздел «Деплой»).