15 KiB
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-5 —
df/субпроцесс на каждом тике — лишняя нагрузка; предпочесть stdlib (NFR-2).