Bug #1: _check_stall возвращался early если alive=False (pipeline ZMQ dead).
Но pipeline может hung без exit'а (ffmpeg process жив но encoder deadlock):
ZMQ не отвечает (alive=False), при этом encoder не emit'ит frames в
mediamtx. Wrapper script не retry'ит (process не exited), никто не
поднимает алерт. Fix: stall check работает ВНЕ зависимости от alive.
Bug #2: _check_stall возвращался early если /live publish session
отсутствует в mediamtx /v3/rtspsessions/list. Pipeline мог никогда
не подключиться (или TCP push session дропнулся). Treat as "frozen
at 0 bytes" — stall alert fire'ится через N polls.
Bug #3 (bonus): _check_instance логировал "lost" но не emit'ил MQTT
event. HA не видела алертов. Fix: добавлен on_event call для pipeline_lost
и pipeline_restored (paired с уже существующими pipeline_stalled /
pipeline_unstalled).
Verified на сегодняшнем incident: pipeline encoder hung 4 hours,
никто не реагировал. После fix monitor emit'ит lost+stalled события
через 12 секунд → MQTT-listener (HA automation) может сделать
docker restart cuda-grid-pipeline.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Resilience improvement — раньше pipeline mог hung без exit (NVENC stuck,
output broken pipe), Docker restart policy не triggered. Никакой alert.
Now: poll mediamtx /v3/rtspsessions/list каждые N sec, track publish session
inboundBytes. Не растёт 3 polls (~9 sec) → emit MQTT 'pipeline_stalled' event
(через dispatcher.on_event = mqtt.publish_event). User / Home Assistant
automation решает что делать (restart container, notify).
Wired:
pipeline_monitor.on_event = mqtt.publish_event # __main__.py
Bytes started growing again → emit 'pipeline_unstalled'.
Alert single-shot: пока stalled flag set, no dup alerts. Reset когда
bytes counter растёт.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bug: restore триггерился сразу после detected "ZMQ ping success", но
filter graph внутри ffmpeg ещё в startup phase — accepts ZMQ commands
но overlay add не fully apply'ются. Часть overlays lost silently.
Симптом: monitor logs "restore_done overlays=6", но pipeline ZMQ probe
показывает filter имеет только 2-3 overlays. Grafana / alerts_chat /
frigate event overlays отсутствуют → user sees пустые cells / strip.
Fix: asyncio.sleep(2.0) перед re-pushing layout + overlays. Pipeline
filter всегда успевает finish init за 2 sec (verified повторными
restart tests).
Cost: 2-sec задержка между detect + visible restore. Acceptable —
total recovery time pipeline crash → fully visible на TV ≈ 5-7 sec.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pipeline filter state (overlays, layout, cell_map, audio) живёт в RAM
ffmpeg process. При recreate container (compose up, OOM, NVENC crash,
config change) state lost — controller'у нужно re-push.
Раньше user'у приходилось вручную:
curl POST /layout/.../set
docker restart cuda-grid-controller # для browser/dynamic re-register
Теперь автоматизировано:
PipelineMonitor polls ZMQ каждые 3 sec (no-op set_layout).
On timeout/error → mark instance lost.
First success after lost → trigger restore:
1. set_layout к state.active_layout
2. set_audio_output_enabled к state.audio_output_enabled
3. re-push все overlays из state.overlays
4. browser/dynamic/frigate hooks: mark_all_unregistered() —
их loops автоматически re-add на next iteration
Verified test: docker restart cuda-grid-pipeline → 10 sec downtime →
monitor logs lost+restored+restore_done с count=6 overlays.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>