Files
healthlog/docs/tasks/items/shutdown-and-migration-traces.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

55 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🐞 Развести бюджеты остановки и оставить следы миграции в логе
- **Тип:** fix
- **Категория:** Инфра
- **Зачем:** Долгий запрос чтения съедает бюджет остановки, и WARN обвиняет воркер свёртки; миграция молчит и не прерывается SIGTERM
- **Теги:** goal:deploy
Две находки эксплуатационного и идиоматического проходов ревью каталога. Обе
существовали и раньше, но достижимыми их сделал первый маршрут чтения:
`GET /api/v1/metrics` — первый обработчик, способный законно работать заметное
время.
**Бюджет остановки один на оба этапа.** `shutdownCtx` в `runServe` передаётся и
в `srv.Shutdown`, и в ожидание фонового воркера. `Shutdown` ждёт, пока
обработчики вернутся; контексты обработчиков он при этом не отменяет
(`BaseContext` не задан), так что долгий запрос каталога может съесть бюджет
целиком. Дальше `select` видит два готовых случая и выбирает равновероятно: база
закрывается или нет от запуска к запуску, а в лог уходит
`shutdown budget exceeded stage=fold-worker` — обвинение воркеру, который бюджета
не превышал. Цена именно в диагнозе: этот `WARN` означает «доставка осталась
`pending`, данные под вопросом», и ложное срабатывание обесценивает настоящее.
Чинится двумя движениями: собственный `context.WithTimeout` второму этапу вместо
исчерпанного первого, и `BaseContext`, производный от контекста жизненного цикла,
чтобы долгий запрос об остановке узнавал.
**Цена этой ветки выросла** (change `cena-chitayushchego-marshruta`): база в ней
не закрывается, а значит не закрывается и закреплённое соединение версии
витрины — последнего соединения к базе не наступает, SQLite не делает финального
чекпойнта, и рядом с базой остаётся неразобранный `-wal` до 64 МиБ. Данные целы
(следующее открытие проиграет журнал), но файл базы в этом состоянии нельзя
переносить без его `-wal`. Обвинение в логе при этом стало честнее: этап
называется `background`, а не `fold-worker`, потому что ждут двоих.
**Миграция молчит и не прерывается штатной остановкой.** `store.migrate` не
пишет ни одной записи — ни «начал», ни «закончил», ни длительность, — а первая
строка в логе появляется уже после успешного открытия базы. Если миграция идёт
долго, владелец не отличит «ещё мигрирует» от «зависло» и от «упало»: тишина
одинакова во всех трёх случаях. Плюс `migrate` работает на `context.Background()`,
то есть `SIGTERM` она не видит и ждать придётся 30-секундного `SIGKILL`.
Порчи данных при этом нет: goose оборачивает миграцию в транзакцию, обрыв
откатывает её целиком, и следующий старт повторяет с нуля. Замер на синтетической
копии годового объёма (260 тысяч объектов, 483 МБ): `CREATE INDEX` миграции
`00009` — 297 мс тёплым кешем. То есть сегодня окно тишины — доли секунды;
опасность в том, что оно растёт вместе с витриной незаметно.
Готово, когда `WARN` о превышении бюджета называет виновный этап честно, а в логе
старта видно, что миграции накатывались и сколько это заняло.
Связано: `cmd/healthlog/serve.go`, `internal/store/store.go`, change
`2026-08-02-cena-chitayushchego-marshruta` (архив).
Двигает строку «Завершения» цели: «Остановка сервиса называет виновный этап честно, а накат миграций виден в логе старта».