Алерты GlitchTip: медленный отказ Telegram отвечает 502 и уходит в фон, а не обрывается на 10-й секунде #3555
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3555
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/glitchtip-delivery"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#3157 — доставка алертов GlitchTip: медленный отказ Telegram больше не рвётся на таймауте отправителя
Что было
На приёмнике (
tradein-mvp/backend/app/api/v1/glitchtip.py) у синхронной пересылки в Telegram был таймаут только на один HTTP-запрос (_INTERACTIVE_SEND_TIMEOUT_S=8). После ретранслятора (#3471) попыток стало больше одной: запрос через relay, при отказе ещё один напрямую, пауза 2 с, повтор. В худшем случае это заметно больше 10 с. GlitchTip ждёт ответ ровно 10 с: вglitchtip-workerapps/alerts/webhooks.pysend_webhookстоитaiohttp.ClientTimeout(total=10), аTimeoutErrorтам молча проглатывается.Улики с прода, 16.09.2026, UTC
Проверено 17.09 через Loki, Prometheus и контейнеры, только чтение.
gendesign-caddy-1) записал этот POST на/ops/glitchtip-webhookтак:status=0,duration=10.16,size=0. Отправитель оборвал соединение.tradein-backend:После этого ни «не удалось переслать», ни 502, ни фоновой доставки.
process_start_time_seconds). Рядhttp_requests_total{route="/ops/glitchtip-webhook",status="200"}появился в скрейпе 16:42:58 со значением 1. Другого POST на вебхук в Caddy за 16:40–16:45 нет, так что по всем признакам это тот же запрос. Вторая попытка прошла около 16:42:55, и хендлер досчитал себе 200 уже после того, как отправитель ушёл.RequestResponseCycle.sendприself.disconnectedделаетreturnдо записи access-log, то есть ответ разорванному клиенту просто выбрасывается.Поправка к триажу. В разборе было сказано «алерт пропал, счётчик не инкрементировался». Счётчик инкрементировался как 200, это спрятал сброс при рестарте в 16:41:20. Алерт, скорее всего, дошёл в Telegram примерно через 5 с после того, как GlitchTip сдался. Прямо проверить доставку в Telegram нечем. Сам дефект от этого не меняется: отправитель видит обрыв (
status=0,is_sent=True), приёмник записывает 200 без строки в access-log, и эти три источника расходятся в исходе. А если бы и вторая попытка не прошла, 502 и фоновый повтор отработали бы уже в пустоту.Что сделано
Минимальный дифф в
glitchtip.py:async with asyncio.timeout(_INTERACTIVE_SEND_DEADLINE_S), где потолок 7 с. Запас 3 с до 10 с GlitchTip оставлен на Caddy и чтение тела;TimeoutErrorловится вместе сTelegramErrorи идёт тем же путём:logger.exception,JSONResponse(502)иBackgroundTask(retry_forward_alert). Фоновая доставка работает по воркерной политике (relay, потом напрямую, до 3×5 попыток) и пишет свой итог: «доставлено фоном» или «текст потерян».Цена: попытка, оборванная потолком, могла на самом деле дойти до Telegram, тогда фоновая доставка создаст дубль сообщения. Этот риск at-least-once уже был принят для ретраев
ReadTimeoutв_request.Тесты
tests/test_glitchtip_alert_retry.py::test_slow_telegram_answers_502_before_sender_gives_up. Фейковый клиент зависает на 5 с, потолок уменьшен до 0.2 с. Проверяется: ответ 502; время ответа меньше 2 с; 2 вызоваsend_message(зависшая синхронная попытка и доставка фоном тем же текстом); боевой потолок_INTERACTIVE_SEND_DEADLINE_Sне больше 8 (10 с GlitchTip минус запас).tests/test_glitchtip_alert_retry.py tests/test_glitchtip_webhook.py: 24 passed, rc=0.DATABASE_URL=... uv run python -m pytest tests/ -q -p no:cacheprovider— 6226 passed, 42 skipped, rc=0 (121 с).uv run ruff check app tests: All checks passed, rc=0.ruff format --checkпо двум изменённым файлам: already formatted, rc=0.Фальсификация
Копия исходника лежала в scratchpad, после каждого прогона восстановлена,
diff -qчистый.async with asyncio.timeout(...)заменён наif True:):Хендлер честно ждёт зависший Telegram все 5 с и отвечает 200. Это ровно 16.09.
TimeoutErrorне ловится (except TelegramError:):На проде это был бы 500 без фоновой доставки.
Чего PR не делает (поэтому issue не закрывается)
tradein-backendперезапускался. Такие отказы есть только в access-логе Caddy в Loki, агрегации и алерта по ним нет.alerts_notification(GlitchTip) с исходами приёмника нет. Сверка за 7 суток из триажа (161 против 153) была разовой и ручной.increase()по счётчику теряет запросы на рестартах: 14 сбросов за 7 суток.Это отдельная задача. Лучше всего её решает сверка «alert-ack получил, tradein-backend нет» или правило по access-логу Caddy.
Приёмка на проде (после деплоя, проверить до 2026-09-25)
docker exec tradein-backend grep -c _INTERACTIVE_SEND_DEADLINE_S app/api/v1/glitchtip.py≥ 1. На 17.09 там 0.{container="gendesign-caddy-1"} |~ "glitchtip-webhook"за неделю после деплоя: нет записей соstatus=0иduration ≥ 10. Каждый POST на вебхук даёт 200 или 502.tradein-backendесть «не удалось переслать алерт в Telegram» и затем «доставлено фоном» или «текст потерян».alerts_notificationза неделю минус (200 + 502 в access-логе Caddy) расходится только на окна рестартовtradein-backend.Refs #3157
🤖 Generated with Claude Code