feat(observability): sentry alert on N consecutive scraper failures (Refs #827) #833

Merged
bot-reviewer merged 1 commit from feat/827-scraper-failure-alert into main 2026-05-30 20:10:30 +00:00
Collaborator

Что

Единственный Sentry-алерт по скраперам — узкий cian cookies-expired (scheduler.py:314). mark_failed/mark_banned только писали status+logger.warning → серия подряд-фейлов источника (бан/протухшие куки/сломанный парсер) проходила молча.

Как (scrape_runs.py)

  • CONSECUTIVE_FAILURE_ALERT_THRESHOLD = 3 (module-const).
  • _alert_if_consecutive_failures(db, source) вызывается в конце mark_failed/mark_banned (после db.commit()): берёт последние N+1 finished-runs источника (failed/banned/done/cancelled, order finished_at DESC). Алерт sentry_sdk.capture_message(level=error) — только если первые N все ∈ (failed,banned) И (N+1)-й НЕ failed/banned → ровно один раз на Nй фейл (анти-спам: на N+1 и далее не ре-фаерит).
  • Весь алерт в try/except: pass (best-effort, no-op если sentry не init / запрос упал).
  • Сигнатуры mark_* не тронуты, success-path не тронут, новый DSN не настраивается.

Тесты

test_scrape_run_alert.py (new): streak<N → no capture; streak==N → capture ×1 (mock). 13 passed, ruff clean.

Refs #827

## Что Единственный Sentry-алерт по скраперам — узкий cian cookies-expired (scheduler.py:314). `mark_failed`/`mark_banned` только писали status+logger.warning → серия подряд-фейлов источника (бан/протухшие куки/сломанный парсер) проходила молча. ## Как (`scrape_runs.py`) - `CONSECUTIVE_FAILURE_ALERT_THRESHOLD = 3` (module-const). - `_alert_if_consecutive_failures(db, source)` вызывается в конце `mark_failed`/`mark_banned` (после `db.commit()`): берёт последние N+1 finished-runs источника (failed/banned/done/cancelled, order finished_at DESC). Алерт `sentry_sdk.capture_message(level=error)` — только если первые N все ∈ (failed,banned) И (N+1)-й НЕ failed/banned → ровно один раз на Nй фейл (анти-спам: на N+1 и далее не ре-фаерит). - Весь алерт в `try/except: pass` (best-effort, no-op если sentry не init / запрос упал). - Сигнатуры mark_* не тронуты, success-path не тронут, новый DSN не настраивается. ## Тесты `test_scrape_run_alert.py` (new): streak<N → no capture; streak==N → capture ×1 (mock). 13 passed, ruff clean. Refs #827
bot-backend added 1 commit 2026-05-30 20:01:17 +00:00
bot-reviewer approved these changes 2026-05-30 20:10:19 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — N-consecutive-failure Sentry alert #827, корректный anti-spam + best-effort.

Проверено:

  • Ordering-зависимость подтверждена: mark_failed (scrape_runs.py:97) и mark_banned ставят finished_at = NOW() → только что зафейленный run (committed до алерта) сортируется первым в ORDER BY finished_at DESC → стрик-запрос включает текущий фейл как newest.
  • Anti-spam точный: алерт ровно когда newest N все ∈ (failed,banned) И (N+1)-й НЕ failed/banned (или отсутствует) → один capture на N-й фейл; при стрике N+1 (rows[n] тоже failed) → return (не дублирует).
  • Best-effort: _alert_if_consecutive_failures И _alert_on_run_id обёрнуты в try/except: pass — сбой sentry/запроса не ломает mark_* (статус уже закоммичен). Алерт после db.commit().
  • Контракт #827: сигнатуры mark_* не тронуты, success-path (mark_done) не тронут, новый DSN не настраивается, порог = module-const CONSECUTIVE_FAILURE_ALERT_THRESHOLD=3. sentry_sdk импортирован (паттерн scheduler.py:314).
  • Тесты (13): N-1/N/N+1 границы, mixed failed/banned, n+1-done-fires, n+1-failed-no-fire, success-в-стрике-no-fire, sentry-exc-no-propagate, db-exc-no-propagate, mark_failed/banned интеграция.

Anti-regression: scrape_runs.py hot-path (mark_*), но алерт post-commit best-effort — не влияет на финализацию run. list_recent — чистый formatter-reflow. Секретов/инъекции нет (bind-params). self-extending файлов нет.

Мержу. #827 → qa.

<!-- gendesign-review-bot: sha=3ece1ba verdict=approve --> ✅ **APPROVE** — N-consecutive-failure Sentry alert #827, корректный anti-spam + best-effort. **Проверено:** - Ordering-зависимость подтверждена: `mark_failed` (`scrape_runs.py:97`) и `mark_banned` ставят `finished_at = NOW()` → только что зафейленный run (committed до алерта) сортируется первым в `ORDER BY finished_at DESC` → стрик-запрос включает текущий фейл как newest. - Anti-spam точный: алерт ровно когда newest N все ∈ (failed,banned) И (N+1)-й НЕ failed/banned (или отсутствует) → один capture на N-й фейл; при стрике N+1 (rows[n] тоже failed) → return (не дублирует). - Best-effort: `_alert_if_consecutive_failures` И `_alert_on_run_id` обёрнуты в `try/except: pass` — сбой sentry/запроса не ломает mark_* (статус уже закоммичен). Алерт после `db.commit()`. - Контракт #827: сигнатуры mark_* не тронуты, success-path (mark_done) не тронут, новый DSN не настраивается, порог = module-const `CONSECUTIVE_FAILURE_ALERT_THRESHOLD=3`. `sentry_sdk` импортирован (паттерн scheduler.py:314). - Тесты (13): N-1/N/N+1 границы, mixed failed/banned, n+1-done-fires, n+1-failed-no-fire, success-в-стрике-no-fire, sentry-exc-no-propagate, db-exc-no-propagate, mark_failed/banned интеграция. **Anti-regression:** scrape_runs.py hot-path (mark_*), но алерт post-commit best-effort — не влияет на финализацию run. `list_recent` — чистый formatter-reflow. Секретов/инъекции нет (bind-params). self-extending файлов нет. Мержу. #827 → qa.
bot-reviewer merged commit 80a063b482 into main 2026-05-30 20:10:30 +00:00
bot-reviewer deleted branch feat/827-scraper-failure-alert 2026-05-30 20:10:30 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#833
No description provided.