Heartbeat-уведомления (dead-man-switch)
Heartbeat — пассивный мониторинг по принципу «не позвонил — значит, что-то случилось». Ваш сервис/cron/скрипт регулярно дёргает короткий HTTP-эндпоинт Notifly, а если очередной ping не пришёл за заданное время — вы получаете push-уведомление с настраиваемым текстом.
Это особенно удобно для:
- регулярных cron-задач (бэкапы, импорты, генераторы отчётов);
- демонов, которые «должны быть всегда включены»;
- IoT-устройств, которые «звонят домой»;
- batch-пайплайнов, где важно не «успех», а «успех вовремя».
Как это работает
Заголовок раздела «Как это работает»ваш cron ──ping──▶ POST /heartbeat/ping/<pingToken> (раз в N сек) └─▶ Notifly обновляет last_ping и сдвигает next_check_at
каждую минуту: timer-trigger → notifly-heartbeat-checker └─▶ SELECT WHERE next_check_at <= now AND status IN (ok, pending) для каждого: создать сообщение + push в WS- Хранение — таблица
heartbeatsв YDB Serverless. Один индекс поnext_check_at— checker сканирует только просроченные записи (дёшево). - Проверка — отдельная Cloud Function
notifly-heartbeat, запускается таймер-триггером Yandex Cloud (cron* * * * ? *— раз в минуту). - Уведомление — обычное сообщение Notifly, отправляется через выбранное при создании heartbeat-а канал и доходит до всех ваших клиентов (web, Android, desktop), как любая другая push-нотификация.
Создание heartbeat
Заголовок раздела «Создание heartbeat»Через админку
Заголовок раздела «Через админку»- Откройте app.notifly.ru → Heartbeats.
- Нажмите «Создать heartbeat», заполните:
- Название — для отображения, например «Cron бэкапа базы».
- Канал — через какой канал слать алёрт.
- Интервал (сек) — ожидаемый период между ping-ами (минимум 30).
- Допуск (сек) — сколько ещё ждать после интервала (защита от джиттера).
- Текст alert — что прилетит в push, когда ping пропустит дедлайн.
- Приоритет alert — 0–10, по умолчанию 8 (громкое уведомление).
- Текст recovery — необязательное «всё снова в порядке», когда после алёрта приходит ping.
- Скопируйте Ping URL из таблицы — этим URL будет дёргать ваш скрипт.
Через REST API
Заголовок раздела «Через REST API»curl -X POST "$NOTIFLY_URL/heartbeat" \ -H "Content-Type: application/json" \ -H "X-Gotify-Key: <client-token>" \ -d '{ "appid": 12345, "name": "Cron бэкапа базы", "intervalSec": 3600, "graceSec": 300, "alertTitle": "Бэкап не запустился", "alertMessage": "За последний час cron бэкапа не пришёл — проверьте сервер!", "alertPriority": 9, "recoveryMessage": "Бэкап снова работает." }'В ответе будет pingToken (начинается с H...) и итоговый URL вида
https://<домен>/heartbeat/ping/<pingToken>.
Через MCP (для AI-ассистента)
Заголовок раздела «Через MCP (для AI-ассистента)»Если у вас настроен MCP-сервер Notifly (см. MCP), просто попросите ассистента:
Создай heartbeat для канала «бэкапы», который ждёт ping раз в час с допуском 5 минут, и шлёт «Бэкап не запустился» приоритета 9.
MCP-инструменты: list_heartbeats, create_heartbeat, update_heartbeat,
pause_heartbeat, resume_heartbeat, delete_heartbeat, ping_heartbeat.
Использование ping
Заголовок раздела «Использование ping»Самый минимальный ping — обычный curl без авторизации:
curl -fsS "$NOTIFLY_URL/heartbeat/ping/H<pingToken>" -o /dev/nullpingToken — это и есть аутентификация. Никаких других заголовков не нужно.
Поддерживается и GET, и POST — чтобы можно было дёргать прямо из cron-job
или мониторингов, которые не умеют POST.
*/15 * * * * /usr/local/bin/backup.sh && curl -fsS "$NOTIFLY_URL/heartbeat/ping/H..." -o /dev/nullPing вызывается только при успехе скрипта (благодаря &&). Если бэкап
упал — ping не пройдёт, и через intervalSec+graceSec прилетит alert.
systemd timer
Заголовок раздела «systemd timer»В service-юните регулярного таймера:
[Service]Type=oneshotExecStart=/usr/local/bin/backup.shExecStartPost=/usr/bin/curl -fsS https://your-notifly/heartbeat/ping/H... -o /dev/nullExecStartPost запускается только если основной ExecStart завершился успешно.
import requests, subprocess
subprocess.check_call(["/usr/local/bin/backup.sh"])requests.post(f"{NOTIFLY_URL}/heartbeat/ping/{TOKEN}", timeout=10)Node.js
Заголовок раздела «Node.js»import {execSync} from 'child_process';execSync('/usr/local/bin/backup.sh');await fetch(`${NOTIFLY_URL}/heartbeat/ping/${TOKEN}`, {method: 'POST'});Состояния heartbeat
Заголовок раздела «Состояния heartbeat»| Состояние | Что значит |
|---|---|
pending | Создан, ни одного ping ещё не приходило |
ok | Последний ping в пределах интервала |
alerting | Дедлайн пропущен, alert уже отправлен |
paused | Проверка отключена (через UI или POST /heartbeat/:id/pause) |
После отправки алёрта heartbeat не «спамит» — следующая проверка отложена ещё
на intervalSec + graceSec секунд. Когда придёт первый ping после alerting —
heartbeat вернётся в ok и (если задано) уйдёт recovery-уведомление.
REST API
Заголовок раздела «REST API»| Метод и путь | Авторизация | Назначение |
|---|---|---|
GET /heartbeat | client-token | список heartbeat-ов |
POST /heartbeat | client-token | создание |
PUT /heartbeat/:id | client-token | обновление |
DELETE /heartbeat/:id | client-token | удаление |
POST /heartbeat/:id/pause | client-token | приостановить проверки |
POST /heartbeat/:id/resume | client-token | возобновить проверки |
GET/POST /heartbeat/ping/:token | публичный | ping (token = аутентификация) |
Почему YDB, а не S3?
Заголовок раздела «Почему YDB, а не S3?»Архитектурно проверка «у каких heartbeat-ов next_check_at ≤ сейчас» — это
индексный point-query, а не сканирование. В YDB Serverless у нас
INDEX heartbeats_next_check_idx и одна короткая транзакция на минуту
(несколько Request Units). В S3 пришлось бы делать LIST (1 запрос) +
GET (по запросу на каждый объект), причём оплата у Object Storage идёт
за каждый запрос, и таких запросов было бы много даже без алёртов.
Heartbeat-ов в проекте быстро становится десятки и сотни — поэтому YDB
заметно дешевле и быстрее.