- каждая запись каталога задач получила тип вместо тега kind: и префикса заголовка; секция роадмапа «Разработка» стала «Сопровождением», порядок секций канонический - поправлены протухшие факты: нереализованные маршруты Read API, MCP и `healthlog import`, словарь слоёв в инварианте, семантика гейта по покрытию диффа, периметр перестал дублировать security.md - замер слияния переведён с находки 49 на находку 54, заполнены Purpose спек storage и parsing
6.1 KiB
✨ Отдавать состояние сервиса маршрутом /stats
- Тип: feature
- Категория: Инфра
- Зачем: Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
- Теги: goal:observability
Тихо сломавшаяся автоматизация — главный эксплуатационный риск коллектора: данные просто перестают приходить, и заметить это можно только по молчанию. Расписание HAE — пожелание, а не гарантия (находка 28), так что молчание случается штатно.
/stats отвечает на «жив ли поток» без чтения логов: последняя доставка по
каждой автоматизации, счётчики за сутки, тишина в часах, доля доставок с
ошибкой разбора, строки без кода в словаре категориальных значений.
Готово, когда по одному запросу видно, какая из автоматизаций замолчала и когда.
Отдельной строкой — отставание фоновой свёртки: длина очереди
(parse_status = 'pending') и возраст самой старой неразобранной доставки.
Сегодня об этом говорят только две метки в логе (WARN «доставка ждала свёртки
дольше пяти минут» и INFO о размере задолженности при старте), а /healthz
статичен и здорового сервиса от сервиса с сотней несвёрнутых тел не отличает.
Пришло из задачи «Разнести ответ приёма и свёртку доставки»: там числа
намеренно не заводились, чтобы не предрешать форму счётчиков этой задачи.
Длина очереди обязана быть видна и без WARN. После миграции, переводящей
доставки в pending, весь исторический бэклог встаёт в очередь перед свежими
доставками, а warnLag на это время намеренно подавлен (startupDone) — то
есть отставание по конструкции не WARN-ится ровно тогда, когда оно максимально,
и бэклог идёт молча при зелёном /healthz. Пришло из дозакрытия находок ревью
по слиянию сущностей (проход ops, находка O1); оракула нет — он потребовал бы
десятков тысяч доставок.
Активное уведомление — отдельная задача, здесь только факт.
Что добавил каталог рода агрегации. Реальный сценарий поломки измерения — не
противоречие свидетельств (его на корпусе не бывает), а их исчезновение: владелец
переставил автоматизацию HAE, минутный слой перестал приходить, метрики одна за
другой уезжают в unknown, Read API перестаёт агрегировать — и в логах ноль
событий. Сюда же вторая половина: пять разных причин непригодности часа
(две точки у часового объекта, невыровненная метка, нет числа, мало минутных,
неразличимость) схлопнуты в одну разность hours − compared, поэтому «HAE
переименовал поле точки» неотличимо от «данных мало». Оба сигнала естественно
живут в /stats: число метрик по родам и число метрик с compared == 0 при
непустом окне.
Корреляция у контура чтения. В записи http request нет ни идентификатора
запроса, ни адреса клиента: жалобу потребителя не сопоставить с записью, а
выгрузку каталога посторонним — не отличить от планового опроса агента. У приёма
корреляция есть (delivery_id), у чтения аналога нет.
Обслуживание журнала WAL тоже спрашивается здесь. Признак «журнал не
разбирается» (чекпойнт по таймеру, change cena-chitayushchego-marshruta)
живёт одной строкой WARN в ротируемом docker-логе: состояние держится днями, а
сказано о нём один раз. Вопрос «журнал сейчас разбирается?» сегодня не имеет
ответа нигде, кроме df. В /stats просятся последний исход чекпойнта (когда,
сколько страниц лежит и сколько перенесено) и — тем же полем — доля ответов
чтения, которые удалось подписать ETag: механизм условного запроса может
перестать окупаться под плотным потоком, и снаружи это неотличимо от нормы.
Двигает строку «Завершения» цели: «Состояние сервиса — последняя доставка, счётчики, тишина — читается одним запросом».