Перейти к основному содержимому

Retry policy

Жизненный цикл одной доставки

emit_event() ──► в Queue


dispatcher worker


attempt 1 ── delay 1s ──► fail?
│ → attempt 2 ── delay 5s ──► fail?
│ → attempt 3 ── delay 30s ──► fail?
│ → attempt 4 ── delay 5min ──► fail?
│ → attempt 5 (без паузы после) ──► fail?


failure_count++ (после 5 подряд → suspend 24ч)

Что считается success

ResponseAction
2xxsuccess, не retry. failure_count=0
410 Gonefail, retry. Логируется как bot_webhook_gone, но webhook НЕ отключается — обрабатывается как обычный провал (ретрай + счётчик до suspend)
4xxfail, retry. Часто = плохая HMAC подпись или auth issue
5xxfail, retry. Server-side ошибка
Network error / timeoutfail, retry

Delays между retries

Паузы применяются МЕЖДУ попытками: 1s, 5s, 30s, 5min (после 5-й попытки паузы нет).
Итого ~5.6 минут на один цикл из 5 попыток.

В таблице задержек есть пятое значение 30min, но оно фактически не применяется: пауза выдерживается только между попытками, поэтому после финальной 5-й попытки паузы нет.

После полного cycle'а (5 attempts, всё провалилось) — failure_count++.

Auto-suspend

Логика простая, без многоступенчатой лестницы: считается число подряд неуспешных циклов доставки. Как только failure_count достигает 5, webhook ставится на паузу на фиксированные 24 часа (suspended_until = now + 24ч).

УсловиеДействие
failure_count < 5webhook активен, доставки продолжаются
failure_count >= 5suspended_until = now + 24 часа

Когда suspension истекает, бэк опять пробует доставку. Если она снова проваливается — failure_count инкрементируется дальше, и при следующем достижении порога webhook опять уходит в suspend на 24 часа.

Никаких промежуточных ступеней 5 мин / 30 мин / 2 ч / 6 ч нет: либо webhook активен, либо (после 5 подряд провалов) спит ровно 24 часа.

Что сбрасывает failure_count

  • Любая успешная доставка (2xx) → failure_count = 0. Счётчик считает только подряд идущие провалы, поэтому один успех полностью обнуляет его.

Это единственный способ сброса: ручного re-enable или эндпоинта обнуления failure_count в bots API сейчас нет (роутер вебхуков — только POST/GET/DELETE). Suspend при этом не ставит enabled=False, а ограничивает доставку через suspended_until.

Webhook secrets и подпись

Сейчас webhook secret хранится в БД только в виде хэша, а plaintext, которым подписываются доставки (X-Reasonspace-Signature), держится в памяти процесса dispatcher'а. Шифрование секрета at-rest — запланированный TODO (Phase 4.5), ещё не задеплоено.

Практическое следствие: после рестарта или редеплоя API plaintext-секрет теряется, и доставки временно идут без заголовка X-Reasonspace-Signature (unsigned), пока секрет не будет задан заново — для этого пересоздайте webhook (DELETE + POST).

Не полагайтесь на гарантированное наличие подписи: на стороне бота обрабатывайте отсутствие X-Reasonspace-Signature как отдельный случай (например, в dev пропускать проверку с предупреждением, а в prod — решать по своей политике). Подробнее о проверке подписи — в разделе Подпись webhook'ов.

Idempotency

Один event может прилететь несколько раз (network retry). X-Reasonspace-Delivery header содержит uniq UUID. Храни последние ~10000 delivery_id в local cache и дропай dup'ы.

SEEN_DELIVERIES = LruCache(maxsize=10_000)

@app.post("/webhook")
async def webhook(request):
delivery_id = request.headers["X-Reasonspace-Delivery"]
if delivery_id in SEEN_DELIVERIES:
return {"ok": True, "duplicate": True}
SEEN_DELIVERIES[delivery_id] = True
# ... handle ...

SDK делает это автоматически: дедуп идёт по заголовку X-Reasonspace-Delivery (с фолбэком на поле id из тела события).

Observability

Отдельных метрик вида reasonspace_bot_webhook_* пока нет — не стройте на них дашборды. Платформа отдаёт только стандартный эндпоинт /metrics с generic HTTP-метриками.

Состояние доставок отражается в структурированных логах dispatcher'а — по ним можно строить алерты. Реальные события лога:

bot_webhook_queue_full_drop # очередь переполнена, событие отброшено
bot_webhook_gone # endpoint вернул 410 Gone
bot_webhook_auto_suspended # webhook ушёл в suspend (5 подряд провалов)
bot_webhook_url_unsafe # URL не прошёл SSRF-guard
bot_webhook_delivery_network_fail # network error / timeout при доставке

Email-уведомление owner'у при auto-suspend — в roadmap; сейчас факт suspend фиксируется только в логе (bot_webhook_auto_suspended).