Files
healthlog/docs/backlog/poryadok-zhurnala-na-priyome.md
T
av 63bffe2865 Приём отвечает 200 до свёртки, свёртку ведёт фоновый воркер
- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`,
  канал несёт только бит «есть работа». Переполнять нечего, падение процесса
  очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а
  не отдельный код. Классификация исхода общая с пересборкой журнала.
- Исход разбора начал отражать доставку, а не обстоятельства: отмена и
  занятость базы статус не меняют (иначе конкуренция за базу выводила бы
  доставку из очереди навсегда), паника свёртки больше не валит процесс, а
  учёт доставки идёт через транзакцию с повторами.
- Длинный бюджет ответа выдан маршруту приёма, а не всему серверу:
  `write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с
  остальных маршрутов.
2026-08-02 11:01:42 +03:00

5.8 KiB
Raw Blame History

Порядок журнала при конкурентных приёмах

Приоритет: блокеры

Вынут ревью кода задачи «Разнести ответ приёма и свёртку доставки» (профиль deep, враждебный проход, находка с построенным путём и прогоном).

Что решить

Метка received_at доставки фиксируется в момент выпуска ULID — до записи тела в архив и до вставки строки учёта. Порядок, в котором строки становятся видимыми воркеру, порядку меток не подчиняется: между выпуском идентификатора и коммитом строки проходит запись тела (измерено 184 мс на 62 МиБ) плюс ожидание занятой базы (до пяти секунд, а с повторами транзакции дольше).

Путь построен и прогнан:

  1. Широкая доставка A автоматизации X получает received_at = T1 и уходит писать тело.
  2. Узкая доставка B той же автоматизации (T2 > T1, только sleep_analysis, плотных метрик нет) успевает закоммитить строку первой и будит воркер.
  3. Воркер видит только B, сворачивает её, наследовать слой не от кого → ErrLayerUnknownfailed.
  4. failed фоновая свёртка не подбирает никогда. Точки B в витрину не попадут.

Измерено на фикстурах: живой приём даёт B=failed и ноль часов sleep_analysis/minute; журнальный порядок — B=parsed и два часа. То есть живое состояние расходится с тем, что даст healthlog reindex, и расхождение молчит: уровень лога у этого исхода WARN, такой же, как у штатного «у этой автоматизации плотных метрик не бывает».

Это не регресс — прежде свёртка шла в порядке завершения обработчиков, то есть было хуже. Изменение окно сузило и назвало предел в спеке приёма; вопрос в том, закрывать ли его совсем.

Варианты и цена

а. Резервировать строку учёта в начале Accept (до записи тела), дописывая raw_path/bytes/sha256 после. Тогда видимость строки монотонна вместе с received_at. Цена: ломается инвариант «тело на диск раньше строки учёта», заведённый ровно затем, чтобы не было учтённой доставки без данных; появляется новое состояние «строка есть, тела ещё нет», которое обязаны понимать пересборка и ретеншен.

б. Откладывать свёртку доставки, пока она не «устоялась» — не сворачивать моложе N секунд. Цена: задержка N на каждую доставку и произвольное N: окно занятости базы измерено до пяти секунд и зависит от нагрузки, так что N честно не выбрать.

в. ErrLayerUnknown в живом пути не выводит доставку из очереди — ограниченное число повторов, потом failed. Цена: колонка счётчика попыток (миграция) и политика «сколько попыток достаточно»; зато лечит и прочие случаи «предшественница ещё не доехала». Требует правки спеки хранения («отказ разбора ⇒ failed»).

г. Ничего не делать, оставив предел названным в спеке. Цена: редкая, молчаливая потеря точек у автоматизаций без плотных метрик; лечится healthlog reindex с остановкой сервиса и ручной подменой базы, но узнать о необходимости неоткуда — счётчика failed в рантайме нет.

Что заблокировано

Ничего: задача про разнесение ответа и свёртки доведена до конца в объявленных границах, предел записан в спеке приёма. Заблокировано только закрытие предела.

Смежно: пока предел жив, полезно уметь сверять живую витрину с пересборкой — reindex уже печатает оба отпечатка, но по расписанию их никто не сравнивает.

Рекомендация

(в), но не раньше /stats: сперва должно стать видно, сколько доставок числится failed и как давно, — иначе повторы будут лечить болезнь, которую никто не наблюдает. До тех пор — (г) с уже записанным пределом.