bug(scrapers): NspdLiteError HTTP 500 на nspd.gov.ru WMS — нужна retry policy для transient ошибок #252

Closed
opened 2026-05-17 06:14:48 +00:00 by lekss361 · 1 comment
Owner

Симптом

GlitchTip BACKEND-8, 10, 11, 12, 14, 16, 17, 24, 36, 37 (10 issues, ~12 events). harvest_quarter падает с NspdLiteError: HTTP 500 от NSPD WMS endpoints (https://nspd.gov.ru/api/aeggis/v4/879243/wms и др.).

Пример event id=37 (cad 66:41:0001001):

GET .../36048/wms → 200 OK
GET .../36049/wms → 200 OK
GET .../875838/wms → 200 OK
GET .../879243/wms → 500 InternalServerError  ← raises NspdLiteError

NSPD возвращает <ServiceException code="InternalSe..."> XML. Это внешний transient bug в WMS layer на стороне NSPD — конкретные layer_id=879243 иногда (~5-10%) возвращает 500, иногда 200.

Что нужно

Retry-with-backoff policy в nspd_lite.py / nspd_client.py для WMS calls:

  1. Transient 500 detectionNspdLiteError с http_status=500 и body содержащим ServiceExceptionReport → retryable
  2. Exponential backoff — 1s, 2s, 5s (3 attempts)
  3. Если все 3 attempts failed — пропустить только этот layer, не fail entire harvest. harvest_quarter должен записать layer_X_failed=true в nspd_quarter_dumps.harvest_meta и продолжить с остальными layers.
  4. Sentry severitywarning вместо error для каждого retried, error только если все 3 attempts провалились

Текущая логика (предполагаемая): первый 500 → exception → задача fails → весь quarter dump потерян.

Reference

  • backend/app/services/scrapers/nspd_lite.py — NspdLiteError raising
  • backend/app/services/scrapers/nspd_client.py_wms_feature_info (стек: code.namespace)
  • backend/app/workers/tasks/nspd_sync.py harvest_quarter — orchestration
  • Tenacity pattern: см. velocity.py retry pattern (если есть)

Acceptance

  • 3-retry policy с exponential backoff для WMS 500 errors
  • Partial harvest success: один failed layer не killует весь dump
  • nspd_quarter_dumps.harvest_meta отмечает skipped layers
  • Tests: mock httpx что для layer N первый запрос 500, второй 200 — retry работает; первые 3 запроса 500 — layer skipped, dump committed

Severity

🟡 P2 — снижает успех harvest rate (~5-10% partial misses) но не блокирует основной flow. Особенно важно для bulk harvest 2400 квартов когда даже 5% = 120 пропусков.

(GlitchTip discovery 2026-05-17, recurring pattern across 10 events за 18 hours)

## Симптом GlitchTip **BACKEND-8, 10, 11, 12, 14, 16, 17, 24, 36, 37** (10 issues, ~12 events). `harvest_quarter` падает с `NspdLiteError: HTTP 500` от NSPD WMS endpoints (`https://nspd.gov.ru/api/aeggis/v4/879243/wms` и др.). Пример event id=37 (cad `66:41:0001001`): ``` GET .../36048/wms → 200 OK GET .../36049/wms → 200 OK GET .../875838/wms → 200 OK GET .../879243/wms → 500 InternalServerError ← raises NspdLiteError ``` NSPD возвращает `<ServiceException code="InternalSe...">` XML. Это **внешний transient bug** в WMS layer на стороне NSPD — конкретные `layer_id=879243` иногда (~5-10%) возвращает 500, иногда 200. ## Что нужно Retry-with-backoff policy в `nspd_lite.py` / `nspd_client.py` для WMS calls: 1. **Transient 500 detection** — `NspdLiteError` с `http_status=500` и body содержащим `ServiceExceptionReport` → retryable 2. **Exponential backoff** — 1s, 2s, 5s (3 attempts) 3. **Если все 3 attempts failed** — пропустить **только этот layer**, не fail entire harvest. `harvest_quarter` должен записать `layer_X_failed=true` в `nspd_quarter_dumps.harvest_meta` и продолжить с остальными layers. 4. **Sentry severity** — `warning` вместо `error` для каждого retried, `error` только если все 3 attempts провалились Текущая логика (предполагаемая): первый 500 → exception → задача fails → весь quarter dump потерян. ## Reference - `backend/app/services/scrapers/nspd_lite.py` — NspdLiteError raising - `backend/app/services/scrapers/nspd_client.py` — `_wms_feature_info` (стек: `code.namespace`) - `backend/app/workers/tasks/nspd_sync.py` `harvest_quarter` — orchestration - Tenacity pattern: см. `velocity.py` retry pattern (если есть) ## Acceptance - [ ] 3-retry policy с exponential backoff для WMS 500 errors - [ ] Partial harvest success: один failed layer не killует весь dump - [ ] `nspd_quarter_dumps.harvest_meta` отмечает skipped layers - [ ] Tests: mock httpx что для layer N первый запрос 500, второй 200 — retry работает; первые 3 запроса 500 — layer skipped, dump committed ## Severity 🟡 P2 — снижает успех harvest rate (~5-10% partial misses) но не блокирует основной flow. Особенно важно для bulk harvest 2400 квартов когда даже 5% = 120 пропусков. (GlitchTip discovery 2026-05-17, recurring pattern across 10 events за 18 hours)
Author
Owner

Status check 2026-05-23

Issue остаётся в силе. После security pivot (PRs #426-#442) NSPD scrapers retry-policy не менялась.

Priority после pilot demo 28.05: medium — без retry-policy transient 500 ломают bulk runs.

Привязка: добавлен в umbrella epic(infra): Pilot infra status 2026-05-23 — после security pivot.

## Status check 2026-05-23 Issue остаётся в силе. После security pivot (PRs #426-#442) NSPD scrapers retry-policy не менялась. **Priority после pilot demo 28.05**: medium — без retry-policy transient 500 ломают bulk runs. Привязка: добавлен в umbrella `epic(infra): Pilot infra status 2026-05-23 — после security pivot`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#252
No description provided.