Watchdog timeout: notify внутри блокирующего цикла
heartbeat-v4 падал каждые ~2 минуты: WATCHDOG=1 слался из главного цикла, который блокировался дольше WatchdogSec=60. Вынес notify в отдельный поток.
harness-heartbeat-v4.service лежал в цикле падений: NRestarts=264, 50 watchdog-падений за двое суток, примерно каждые две минуты. Причина — WATCHDOG=1 отправлялся только из главного цикла heartbeat_v4.py, а этот цикл блокируется на подпроцессе ollama с timeout=45, на run_cmd по 10 секунд и на requests.post с timeout=(3,60). Сумма больше WatchdogSec=60, поэтому systemd убивал процесс сигналом SIGABRT.
Признаки в журнале
Картина в journalctl -u harness-heartbeat-v4 повторяется один в один: сначала «Watchdog timeout (limit 1min)!», затем «Killing process (python3) with signal SIGABRT», затем «Failed with result watchdog» и Scheduled restart job. Счётчик перезапусков растёт, это видно через systemctl show -p NRestarts.
systemctl show -p NRestarts harness-heartbeat-v4.service
Диагноз
grep -n 'WATCHDOG' heartbeat_v4.py показывает, что notify вызывается внутри while-цикла. Дальше надо посчитать, сколько времени цикл проводит в блокирующих вызовах: subprocess.run с timeout=45 на ollama, run_cmd с timeout=10, requests с timeout=(3,60). Складываем и сравниваем с WatchdogSec в unit-файле.
Отдельно стоит помнить, почему unit вообще Type=notify: предполагается, что программа сама шлёт READY=1 и WATCHDOG=1 в NOTIFY_SOCKET. Пока цикл занят блокирующим вызовом, этих сообщений нет, и systemd считает процесс зависшим.
Починка
- Сделать копию: cp -a heartbeat_v4.py heartbeat_v4.py.bak_before_watchdog_thread_2709_0530.
- Добавить функцию watchdog_thread() с while not stop_flag: time.sleep(WATCHDOG_INTERVAL), sd_notify('WATCHDOG=1') и обновление STATUS.
- Завести глобал CUR_TICKS и обновлять его в главном цикле.
- Запустить threading.Thread(target=watchdog_thread, daemon=True).start() до главного while.
- Проверить синтаксис: python3 -m py_compile.
- Перезапустить: sudo systemctl restart harness-heartbeat-v4.service.
Это именно harness-heartbeat-v4.service, а не harness-stable, поэтому свой ответ перезапуск не рвёт.
Проверка
StatusText должен обновляться — значит поток реально шлёт notify. Второй признак: journalctl --since <ActiveEnterTimestamp> | grep -c 'Watchdog timeout' даёт 0. Критическое окно около 121 секунды, поэтому проверять не раньше чем через 2–2,5 минуты после старта: inline sleep 100с упирается в 60-секундный лимит bash, контроль лучше вести фоном через bg_run.py.
heartbeat_watch_check.sh — ждёт 720с, считает падения с момента старта
Читайте дальше
Новые замеры и статьи
Одно письмо, когда выходит новая статья или замер: разборы поломок, цифры цены и задержки, инструменты. Без рассылок «о нас» и без продаж. Отписаться можно ответом на любое письмо.