Files

15 KiB
Raw Permalink Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-063 analysis analyst ready-for-review 2026-06-09 claude-opus-4-8

01 — BRD (бизнес-требования): ORCH-063 — INFRA: мониторинг диска mva154 + алерт при >85%

Work Item: ORCH-063 · Repo: orchestrator (self-hosting) · Стадия: analysis Заказчик: Слава (Владелец/оператор) Тип: INFRA · Приоритет: P1


1. Бизнес-контекст и проблема

1.1. Инцидент (установленный факт)

07.06.2026 диск на хосте mva154 (slin@82.22.50.71) незаметно дорос до 100% и положил весь конвейер: CI стал красным, очередь Gitea застряла. Сбой произошёл тихо — не было ни одного предупреждающего сигнала до полного исчерпания диска. Разбор был ручным и пост-фактум.

1.2. Корневая боль

У оркестратора нет проактивного сигнала о заполнении диска. Диск хоста заполняется накопительно и предсказуемо (git-worktree в /repos/_wt/..., образы Docker, БД ./data/orchestrator.db, логи), но оператор узнаёт о проблеме только когда уже поздно — конвейер всех проектов (self-hosting: orchestrator + enduro-trails из одного инстанса) уже встал.

1.3. Self-hosting контекст (групповой риск)

Прод-инстанс orchestrator (8500) — ОДИН на ВСЕ прод-проекты, с общей БД и общей очередью (docs/operations/INFRA.md). Исчерпание диска роняет конвейер всех проектов сразу. Ранний сигнал (heartbeat-watchdog) — дешёвая страховка от дорогого группового простоя.

1.4. Что нужно (формулировка Владельца)

Heartbeat-watchdog: периодически измерять заполнение диска (df); при превышении порога 85% — слать алерт Славе (Telegram). Сигнал должен прийти заранее, пока есть запас места на ручную/будущую авто-очистку.


2. Объём (scope)

2.1. В объёме

  • Фоновый watchdog-демон (по образцу reconciler/job_reaper, ORCH-053/065): периодически семплит заполнение хост-ФС, на которой живут рабочие данные оркестратора (репозитории, БД, Docker), и при пересечении порога шлёт Telegram-алерт оператору.
  • Конфигурируемый порог (дефолт 85%), период опроса, kill-switch.
  • Анти-спам: алерт по факту пересечения порога + ограниченное по частоте повторение, пока заполнение выше порога (а не на каждом тике); сообщение о возврате «ниже порога» (recovery).
  • Наблюдаемость последнего замера/состояния алерта в GET /queue (read-only).
  • never-raise: любой сбой watchdog не влияет на конвейер.

2.2. Вне объёма (явно, не делать)

  • Авто-очистка / garbage collection диска (прунинг старых worktree, образов, логов, vacuum БД) — отдельная задача; ORCH-063 только сигнализирует, не лечит.
  • Интеграция с внешними системами мониторинга (Prometheus/Grafana/Zabbix), метрики/экспортёры.
  • Алерт-каналы кроме существующего Telegram (send_telegram).
  • Мониторинг ресурсов кроме диска (CPU/RAM/inode — возможное расширение, не сейчас; inode — кандидат на follow-up, см. §8 R-4).
  • Мониторинг нескольких хостов / удалённый сбор (только локальный хост текущего инстанса).
  • Изменение STAGE_TRANSITIONS, реестра QG_CHECKS, стадий конвейера, схемы БД-контрактов.

3. Заинтересованные стороны

  • Владелец/оператор (Слава): получает алерт, выполняет ручную очистку/реакцию; принимает результат.
  • Self-hosting прод (orchestrator): обслуживает enduro-trails из того же инстанса — watchdog не должен мешать/ронять конвейер (изоляция через never-raise).
  • Все прод-проекты: косвенные бенефициары — ранний сигнал предотвращает групповой простой.

4. Бизнес-требования (BR)

ID Требование Связь
BR-1 Оркестратор периодически (heartbeat) измеряет заполнение хост-файловой системы, на которой растут его рабочие данные (репозитории /repos, БД /app/data, Docker). FR-1, AC-1
BR-2 При достижении/превышении порога заполнения (дефолт 85%) оператор получает Telegram-алерт с действенными деталями: точка монтирования/путь, занято %, свободно (ГБ/%). FR-2, FR-3, AC-2
BR-3 Анти-спам: алерт шлётся при пересечении порога (переход «ниже→на/выше»), а далее повторяется не чаще, чем раз в настраиваемый период (re-alert), пока заполнение остаётся выше порога — конвейер/чат не заваливается одинаковыми сообщениями на каждом тике. FR-4, AC-3
BR-4 При возврате заполнения ниже порога состояние алерта сбрасывается и отправляется однократное сообщение восстановления «диск ниже порога» (recovery), чтобы оператор знал, что инцидент снят. FR-4, AC-4
BR-5 Порог, период опроса, период повторного алерта и набор отслеживаемых путей конфигурируемы; есть kill-switch для полного отключения watchdog (нулевая регрессия). FR-5, AC-5
BR-6 never-raise: любая ошибка измерения/отправки алерта/самого демона не роняет и не блокирует конвейер (фоновый поток, изолированный как reconciler/reaper). NFR-1, AC-6
BR-7 Текущее состояние watchdog (последний замер по путям, состояние алерта, время последнего алерта, порог/период) наблюдаемо в GET /queue (read-only). FR-6, AC-7
BR-8 Watchdog стартует/останавливается вместе с приложением (в main.lifespan) и не требует ручного запуска. FR-1, AC-8

5. Нефункциональные требования (NFR)

ID Требование
NFR-1 never-raise / изоляция: watchdog — отдельный daemon-поток (паттерн reconciler/job_reaper); исключение в тике логируется и не прерывает ни поток, ни конвейер.
NFR-2 Дешевизна: замер диска — лёгкая операция (предпочтительно stdlib shutil.disk_usage, без тяжёлого порождения процессов на каждом тике); период опроса по умолчанию — порядка минут (не секунд), чтобы не создавать нагрузки.
NFR-3 Корректность источника замера (self-hosting): измеряется заполнение хост-ФС, а не overlay-ФС контейнера. Контейнер видит хост-разделы через bind-mount'ы (/repos, /app/data); замер обязан отражать раздел(ы), которые реально заполняются на хосте (см. §6).
NFR-4 Нулевая регрессия: при выключенном kill-switch поведение приложения идентично текущему; enduro-trails и конвейер не затрагиваются.
NFR-5 Инварианты неизменны: STAGE_TRANSITIONS, реестр QG_CHECKS, check_*, существующие таблицы-контракты БД — не меняются. Допустимо не вводить новую миграцию (состояние watchdog — best-effort, может жить в памяти).
NFR-6 Self-hosting безопасность: watchdog только читает заполнение и шлёт уведомление — не выполняет действий над диском/контейнером, не рестартит прод.

6. Допущения и ограничения

  • Видимость хост-диска из контейнера. Оркестратор бежит в контейнере с network_mode: host и bind-mount'ами /home/slin/repos → /repos, ./data → /app/data, /var/run/docker.sock (docs/operations/INFRA.md). Замер shutil.disk_usage()/df по смонтированному пути (/repos, /app/data) отражает заполнение хост-раздела, который этот путь подмонтировал — именно той ФС, что переполнилась 07.06. Замер по / (overlay контейнера) нерепрезентативен и не должен использоваться как источник истины.
  • Один заполняющийся раздел. На mva154, вероятно, рабочие данные (/home/slin/repos, ./data, Docker) лежат на одном host-разделе; набор отслеживаемых путей по умолчанию должен покрывать его и при совпадении физического устройства не дублировать алерт (дедуп по устройству — желательное, не блокирующее требование; решение — за архитектором).
  • Best-effort алертинг. Доставка Telegram не гарантирована (та же send_telegram, never-raise); watchdog — ранний сигнал, не SLA-гарантия. Состояние анти-спама может быть in-memory (после рестарта допустим повторный алерт, если всё ещё выше порога — это безопасно).
  • Порог 85% — зафиксирован Владельцем как дефолт; конфигурируем (BR-5) на случай тюнинга.
  • Только сигнал, не лечение. Авто-освобождение места — вне объёма (§2.2).

7. Критерии успеха (резюме; детали — 03-acceptance-criteria.md)

  • AC-1 watchdog периодически измеряет заполнение хост-ФС и стартует с приложением.
  • AC-2 при ≥85% оператор получает Telegram-алерт с действенными деталями.
  • AC-3 анти-спам: один алерт на пересечение + ограниченное повторение, не на каждом тике.
  • AC-4 возврат ниже порога → сброс состояния + recovery-сообщение.
  • AC-5 порог/период/пути/kill-switch конфигурируемы; выключение → нулевая регрессия.
  • AC-6 любой сбой watchdog не роняет конвейер (never-raise).
  • AC-7 состояние watchdog видно в GET /queue.

8. Риски (детали — 10-tech-risks.md, заполняет архитектор)

  • R-1 — замер по неверной ФС (overlay / контейнера вместо хост-раздела) → ложно-низкое заполнение → watchdog «молчит» при реально полном хосте (повтор инцидента 07.06). Митигировать: замер по bind-mount-путям хост-разделов (NFR-3).
  • R-2 — спам-алерты на каждом тике при длительном превышении порога → шум, оператор глохнет к сигналу. Митигировать: анти-спам/cooldown (BR-3).
  • R-3 — порог 85% слишком близок к 100% при быстром росте (один большой build/worktree) → оператор не успевает среагировать. Зафиксирован как дефолт Владельцем; конфигурируемость (BR-5) оставляет рычаг. Возможный follow-up — второй «критический» порог (напр. 95%) с более громким алертом (кандидат, не в объёме).
  • R-4 — исчерпание inode (а не байтов) тоже валит ФС, но не ловится замером по %-байтам. Кандидат на расширение (вне объёма ORCH-063).
  • R-5df/субпроцесс на каждом тике — лишняя нагрузка; предпочесть stdlib (NFR-2).