feat(monitoring): алерты на недоступность БД и исчерпание пулов + дашборд
DatabaseUnavailable (vidconf_db_up == 0, for: 30s, critical) и DbConnectionPoolNearExhaustion/RedisConnectionPoolNearExhaustion (занято > 80% дольше минуты, warning) — сигнал оператору, не автолечение: healthcheck backend'а по решению оператора остаётся мягким, рестарт при недоступной БД оборвал бы WS у всех, кто в конференциях. Дашборд Grafana «БД и пулы соединений» — занятость пулов на графике, следующий нагрузочный тест будут смотреть глазами.
This commit is contained in:
@@ -61,6 +61,89 @@ groups:
|
||||
# см. также алерт QueueGrowing). Проверить
|
||||
# `docker compose ps llm` / `llm-gpu` и `docker compose logs llm`.
|
||||
|
||||
# Состояние БД и пулов соединений (сессия 33, разбор инцидента 07.08 —
|
||||
# `.forcc/session-results/32-loadtest-07-08-debug.md`). `/api/health`
|
||||
# отдавал 200 с `db: false` во время отказа — Prometheus его не скрейпит и
|
||||
# не умеет разобрать JSON-тело, поэтому оба сигнала строятся на метриках
|
||||
# `backend/api/metrics.py`, которые читаются вне основного пула.
|
||||
- name: vidconf-db
|
||||
rules:
|
||||
# `vidconf_db_up` — отдельное соединение вне основного пула
|
||||
# (`core/db.py::check_db_up`), поэтому 0 означает именно «БД не
|
||||
# отвечает», а не «пул занят» (для второго см. DbConnectionPoolNearExhaustion
|
||||
# ниже — раздельные метрики нарочно, см. «Главное требование» промпта
|
||||
# сессии 33). `for: 30s` — два цикла скрейпа (`scrape_interval: 15s`),
|
||||
# чтобы не среагировать на одиночный неудачный `connect()` (сеть/GC-пауза),
|
||||
# но не тянуть с сигналом дольше: это самый критичный алерт в проекте.
|
||||
- alert: DatabaseUnavailable
|
||||
expr: vidconf_db_up == 0
|
||||
for: 30s
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "БД недоступна"
|
||||
description: >-
|
||||
vidconf_db_up == 0 дольше 30 секунд — backend не может открыть
|
||||
отдельное (вне основного пула) соединение с Postgres. НЕ значит
|
||||
автоматически «нужен рестарт контейнера» — по решению оператора
|
||||
от 09.08 healthcheck backend'а остаётся мягким (не хардфейлится
|
||||
на недоступной БД — рестарт-петля в разгар инцидента оборвала бы
|
||||
WS у всех, кто в конференциях), это сигнал оператору, не
|
||||
автолечение. Смотреть
|
||||
`docker compose ps postgres`, `docker compose logs postgres`,
|
||||
`pg_isready`.
|
||||
|
||||
# Раннее предупреждение — тот самый сигнал, которого не хватило
|
||||
# 07.08: пул заполнялся постепенно (idle in transaction 3→8→16→26→35→
|
||||
# 39→40 участников комнаты, см. session 32), а `up{job="backend"}`
|
||||
# ничего не показывал, потому что backend отвечал исправно вплоть до
|
||||
# самого потолка. Порог 80% — предложение из промпта сессии 33,
|
||||
# `for: 1m` — фильтр от секундных всплесков (короткий пик параллельных
|
||||
# запросов рассасывается за секунды, устойчивый рост участников
|
||||
# комнаты — нет). На нагрузочном тесте 07.08 от пересечения 80% до
|
||||
# исчерпания пула прошло по грубой оценке меньше двух минут — порог
|
||||
# НЕ даёт большого запаса и это осознанный компромисс, а не идеал:
|
||||
# цель — успеть до 500-х у пользователей, а не за много минут
|
||||
# заранее. Перепроверить оба числа на следующем нагрузочном тесте
|
||||
# (см. .forcc/session-results/33-db-health-alert.md) и подстроить,
|
||||
# если реальный запас окажется у́же ожидаемого.
|
||||
- alert: DbConnectionPoolNearExhaustion
|
||||
expr: >-
|
||||
(vidconf_db_pool_checked_out
|
||||
/ (vidconf_db_pool_size + vidconf_db_pool_max_overflow)) * 100 > 80
|
||||
for: 1m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Основной пул соединений с БД близок к исчерпанию"
|
||||
description: >-
|
||||
Занято {{ $value | printf "%.0f" }}% основного пула БД дольше
|
||||
минуты (порог 80%). Частая причина в этом проекте — долгоживущие
|
||||
WS-подключения комнат (`api/chat.py`) держат соединение на
|
||||
каждого сидящего в конференции; смотреть
|
||||
`vidconf_db_pool_checked_out` и число открытых WS чата в логах,
|
||||
не только текущую HTTP-нагрузку.
|
||||
|
||||
# Тот же класс отказа, что у пула БД (см. выше), только пул Redis —
|
||||
# закрыт в 0.0.31 (`451c18e`) заданием явного max_connections, но без
|
||||
# метрики занятости прошлый потолок нашёлся только руками на
|
||||
# нагрузочном тесте. Бонус к задаче сессии 33 («потолки в этом
|
||||
# проекте стоят лесенкой»), не отдельно запрошен промптом — пороги
|
||||
# взяты по аналогии с пулом БД, не проверялись отдельным нагрузочным
|
||||
# тестом именно на Redis.
|
||||
- alert: RedisConnectionPoolNearExhaustion
|
||||
expr: (vidconf_redis_pool_in_use / vidconf_redis_pool_max_connections) * 100 > 80
|
||||
for: 1m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Пул соединений Redis близок к исчерпанию"
|
||||
description: >-
|
||||
Занято {{ $value | printf "%.0f" }}% пула Redis дольше минуты
|
||||
(порог 80%). Каждое WS-подключение комнаты держит собственную
|
||||
pub/sub-подписку из этого же пула — смотреть число открытых WS
|
||||
чата, не только команды Celery/кэша.
|
||||
|
||||
# Железо хоста (job `node` — node-exporter). Пороги подобраны под
|
||||
# конкретный сервер 1gb: 8 ГБ RAM, 4 CPU, 50 ГБ диска — если сервер
|
||||
# сменится, пересчитать.
|
||||
|
||||
Reference in New Issue
Block a user