
Heartbeat-мониторинг cron-job'ов: dead-man-switch на FastAPI
cyberscoper 57 минут назад Heartbeat-мониторинг cron-job'ов: dead-man-switch на FastAPI Простой 5 мин 2.4K *nix * DevOps * Системное администрирование * Туториал Привет, Хабр!Обычный uptime-мониторинг проверяет,...
Вот важная новость с фронта ИИ: cyberscoper 57 минут назад Heartbeat-мониторинг cron-job'ов: dead-man-switch на FastAPI Простой 5 мин 2. 4K *nix * DevOps * Системное администрирование * Туториал Привет, Хабр! Обычный uptime-мониторинг проверяет, отвечает ли сервис на запросы.
Cron-job ничего не отвечает — он запускается раз в N часов, делает работу и молча завершается. Если cron перестал запускаться (uptime daemon упал, машина в read-only mode после fsck, disk full) — обычный мониторинг этого не видит. Решение известно с 70-х — паттерн dead-man-switch (он же heartbeat).
Технические детали
Я недавно делал heartbeat-эндпоинты для Valpero. Здесь разберу серверную часть на FastAPI + клиентский bash-pattern, и edge-кейсы которые их ломают. В конце готовый код, который можно адаптировать под свой стек.
КонцепцияОбычный uptime-чек: «спроси у сервиса, всё ли в порядке». Dead-man-switch: «жди что сервис сам скажет всё в порядке, и если перестал говорить — алёрт». Применимо к:cron-job’ам (0 4 * * * backup.
sh)background workers (Celery, Sidekiq, RQ)scheduled lambdaлюбой задаче, которая должна выполниться по расписанию, но не имеет HTTP-endpoint’аJob делает curl на уникальный URL после успешного завершения: 0 4 * * * /opt/scripts/backup. sh && \ curl -fsS > /dev/null Сервер запоминает время последнего ping’а. Если ping не приходит в ожидаемое окно — открывается incident.
Отраслевые последствия
Окно = interval + grace_period. Для daily-job окно ~25 часов. Если в 5:00 следующего дня после 4:00 ping не пришёл — alert.
Серверная часть на FastAPIБазовая модель: from sqlalchemy import Column, Integer, String, Boolean, DateTime from app. database import Base class HeartbeatMonitor(Base): tablename = “heartbeat_monitors” id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) name = Column(String(120), nullable=False) token = Column(String(40), unique=True, nullable=False, index=True) interval_sec = Column(Integer, nullable=False) # 24*3600 для daily grace_sec = Column(Integer, nullable=False) # 3600 = 1h допуск last_ping_at = Column(DateTime, nullable=True) state = Column(String(20), default=“pending”) # pending/up/downinterval_sec + grace_sec — это окно, в которое должен прийти ping. Если не пришёл — state = down.
Endpoint для приёма ping’а: from fastapi import APIRouter, Request, Depends from fastapi. responses import Response from datetime import datetime, UTC from sqlalchemy import select from app. database import get_db router = APIRouter()@router.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.




