- род метрики выводится сверкой минутного слоя с часовым: часовое значение сходится с суммой минутных — накопительная, со средним — мгновенная, иначе `unknown` и свёртка не предлагается вовсе. На живом архиве (123 доставки, 31 метрика) 7 накопительных, 9 мгновенных, противоречащих часов ноль - `GET /api/v1/metrics` под токеном чтения отдаёт единицы, слои с границами и род вместе с основанием измерения; род нигде не хранится — он функция витрины, а витрина функция журнала, устаревать в нём нечему - миграция 00009: покрывающий индекс, чтобы каталог отвечал по учётным колонкам, не разжимая содержимое объектов
42 lines
3.7 KiB
Markdown
42 lines
3.7 KiB
Markdown
# Остановка и миграция: раздельные бюджеты и следы в логе
|
|
|
|
**Приоритет:** средний
|
|
|
|
Две находки эксплуатационного и идиоматического проходов ревью каталога. Обе
|
|
существовали и раньше, но достижимыми их сделал первый маршрут чтения:
|
|
`GET /api/v1/metrics` — первый обработчик, способный законно работать заметное
|
|
время.
|
|
|
|
**Бюджет остановки один на оба этапа.** `shutdownCtx` в `runServe` передаётся и
|
|
в `srv.Shutdown`, и в ожидание фонового воркера. `Shutdown` ждёт, пока
|
|
обработчики вернутся; контексты обработчиков он при этом не отменяет
|
|
(`BaseContext` не задан), так что долгий запрос каталога может съесть бюджет
|
|
целиком. Дальше `select` видит два готовых случая и выбирает равновероятно: база
|
|
закрывается или нет от запуска к запуску, а в лог уходит
|
|
`shutdown budget exceeded stage=fold-worker` — обвинение воркеру, который бюджета
|
|
не превышал. Цена именно в диагнозе: этот `WARN` означает «доставка осталась
|
|
`pending`, данные под вопросом», и ложное срабатывание обесценивает настоящее.
|
|
|
|
Чинится двумя движениями: собственный `context.WithTimeout` второму этапу вместо
|
|
исчерпанного первого, и `BaseContext`, производный от контекста жизненного цикла,
|
|
чтобы долгий запрос об остановке узнавал.
|
|
|
|
**Миграция молчит и не прерывается штатной остановкой.** `store.migrate` не
|
|
пишет ни одной записи — ни «начал», ни «закончил», ни длительность, — а первая
|
|
строка в логе появляется уже после успешного открытия базы. Если миграция идёт
|
|
долго, владелец не отличит «ещё мигрирует» от «зависло» и от «упало»: тишина
|
|
одинакова во всех трёх случаях. Плюс `migrate` работает на `context.Background()`,
|
|
то есть `SIGTERM` она не видит и ждать придётся 30-секундного `SIGKILL`.
|
|
|
|
Порчи данных при этом нет: goose оборачивает миграцию в транзакцию, обрыв
|
|
откатывает её целиком, и следующий старт повторяет с нуля. Замер на синтетической
|
|
копии годового объёма (260 тысяч объектов, 483 МБ): `CREATE INDEX` миграции
|
|
`00009` — 297 мс тёплым кешем. То есть сегодня окно тишины — доли секунды;
|
|
опасность в том, что оно растёт вместе с витриной незаметно.
|
|
|
|
Готово, когда `WARN` о превышении бюджета называет виновный этап честно, а в логе
|
|
старта видно, что миграции накатывались и сколько это заняло.
|
|
|
|
Связано: `cmd/healthlog/serve.go`, `internal/store/store.go`,
|
|
`cena-chitayushchego-marshruta.md`.
|