Close the observability gap between agent_hung (only tracked jobs by jobs.pid)
and orphaned pytest subprocesses the orchestrator launches itself
(merge_gate.retest_branch / coverage_gate.measure_coverage). On a timeout-kill of
the agent (-9, ORCH-109) the grand-child pytest reparents onto tini and keeps
running for days, starving CPU and failing merge-gate re-test — with no alert.
Strictly inside the observer (watchdog/** + the watchdog compose service):
- watchdog/collectors/proc.py: stdlib-only /proc scan (under pid: host),
read-only, never-raise -> []; pure parsers split from I/O (tested on a fake
/proc tree). Never reads /proc/<pid>/environ.
- watchdog/signals.py: pure proc_signals builder, per-entity
("proc_blocking", pid), active iff age_s > proc_age_s; actionable RU detail.
- watchdog/core.py: opt-in tick block (gated on proc_enabled -> zero overhead /
byte-for-byte when off) + RECOVERY synthesis for a vanished process through the
existing decide()/AlertState (no new anti-spam logic).
- watchdog/config.py: WATCHDOG_PROC_{ENABLED(false),AGE_MIN(60),PATTERNS(pytest),
COOLDOWN_S(1800)}; default threshold > max(merge_retest_timeout_s=600,
coverage_run_timeout_s=900) so a legit in-flight run never crosses it.
- docker-compose.yml: pid: host on orchestrator-watchdog ONLY (read-only privilege).
Anti-false-positive and no overlap with agent_hung are by construction (cmdline
scope + age threshold), not fragile cross-namespace PID matching.
Canon synced: WATCHDOG_PROC_* in .env.watchdog.example <-> .env.example block;
documented in LITE_SETUP.md and docs/architecture/README.md (architect). src/**,
/metrics, schema_version, STAGE_TRANSITIONS, QG_CHECKS, check_*, machine-verdict
and the DB schema are untouched; deploy rebuilds only the sidecar, prod
orchestrator is not restarted (NFR-3).
Tests: tests/watchdog/test_proc_blocking_signal.py (TC-01..TC-06),
test_proc_collector.py (/proc parsing), test_tick_proc_blocking_integration.py
(TC-07), plus pid: host and proc-config assertions. Full pytest tests/ green (1930).
Refs: ORCH-111
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
3.1 KiB
Plaintext
53 lines
3.1 KiB
Plaintext
# .env.watchdog — конфигурация sidecar-watchdog (контейнер orchestrator-watchdog).
|
||
# Канонический example (ORCH-102, ADR-001 D5; симметрия .env.example/.env.staging.example).
|
||
#
|
||
# ⚠️ СЕМАНТИКА ФАЙЛА-НОСИТЕЛЯ: sidecar-контейнер читает ТОЛЬКО этот файл
|
||
# (compose: env_file {path: .env.watchdog, required: false}). Ключ WATCHDOG_*,
|
||
# положенный в .env, для sidecar ИНЕРТЕН (его видит лишь контейнер орка).
|
||
# Отсутствие файла НЕ ломает `docker compose up` (required: false); нет токена →
|
||
# fail-safe: watchdog пишет алерты в логи, но не отправляет.
|
||
#
|
||
# Создание на хосте: cp .env.watchdog.example .env.watchdog → заполнить два токена.
|
||
# DO NOT COMMIT реальный .env.watchdog — этот файл только шаблон (зеркало
|
||
# .env.staging.example); реальные значения живут на хосте.
|
||
#
|
||
# Нормативы:
|
||
# * C-1 (ORCH-100): у watchdog СВОЙ Telegram-бот — независимый канал алертов.
|
||
# Переиспользовать токен орка (ORCH_TELEGRAM_BOT_TOKEN) ЗАПРЕЩЕНО: упавший
|
||
# орк не сможет сообщить о себе своим же ботом.
|
||
# * Когерентность порта: WATCHDOG_METRICS_URL следует за прод-портом
|
||
# (ORCH_DEPLOY_PROD_TARGET_PORT) — сменил порт орка → обнови URL здесь.
|
||
# * Key-set этого файла = блок WATCHDOG_* в .env.example (канон ключей);
|
||
# синхронность держит tests/test_lite_setup_doc.py (key-sync, TC-02b).
|
||
# Значения = дефолты watchdog/config.py.
|
||
|
||
WATCHDOG_ENABLED=true
|
||
WATCHDOG_INTERVAL_S=30
|
||
WATCHDOG_HTTP_TIMEOUT_S=5
|
||
WATCHDOG_COOLDOWN_S=1800
|
||
WATCHDOG_METRICS_URL=http://127.0.0.1:8500/metrics
|
||
WATCHDOG_ORCH_DOWN_TICKS=3
|
||
WATCHDOG_MEM_PCT=90
|
||
WATCHDOG_DISK_CRIT_ENABLED=false
|
||
WATCHDOG_DISK_CRIT_PCT=97
|
||
WATCHDOG_DISK_PATHS=/repos,/app/data
|
||
WATCHDOG_AGENT_HUNG_MIN=20
|
||
WATCHDOG_AGENT_CPU_FLOOR=0.01
|
||
WATCHDOG_STAGE_STUCK_MIN=120
|
||
WATCHDOG_QUEUE_DEPTH=20
|
||
WATCHDOG_CONTAINERS=orchestrator
|
||
WATCHDOG_DOCKER_SOCK=/var/run/docker.sock
|
||
WATCHDOG_DEPS=
|
||
# proc_blocking (ORCH-111): opt-in алерт на долго живущий осиротевший тест-процесс
|
||
# (pytest), репарентированный на хост. Требует `pid: host` на сервисе
|
||
# orchestrator-watchdog (compose) — привилегия только у наблюдателя, read-only.
|
||
# Дефолт-off → нулевая регрессия. PROC_AGE_MIN ОБЯЗАН превышать
|
||
# max(merge_retest_timeout_s=600, coverage_run_timeout_s=900)/60 = 15 мин, иначе
|
||
# легитимный прогон даст ложный алерт. 60 мин = 4× запас.
|
||
WATCHDOG_PROC_ENABLED=false
|
||
WATCHDOG_PROC_AGE_MIN=60
|
||
WATCHDOG_PROC_PATTERNS=pytest
|
||
WATCHDOG_PROC_COOLDOWN_S=1800
|
||
WATCHDOG_TG_BOT_TOKEN=
|
||
WATCHDOG_TG_CHAT_ID=
|