feat(tradein): per-anchor watchdog timeout in city sweeps (#880) #881
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#881
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/880-anchor-watchdog"
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?
Summary
run_{avito,cian,yandex}_city_sweephad no per-anchor watchdog: a stalled HTTP request (proxy/connection hang without an effective session timeout) blocked the asyncio task forever — heartbeat froze mid-anchor,anchors_donestopped, the run stayedrunninguntil the 6h zombie-detect, and subsequent anchors never ran. Live-repro: cian sweep #24 hung on anchor 3, #29 on anchor 1 (data before the hang was saved, but the sweep never finished).Fix (cheap, independent of the Celery rework #581):
ANCHOR_TIMEOUT_SEC = 240. Each anchor's processing is wrapped inasyncio.wait_for(<anchor coro>, timeout=ANCHOR_TIMEOUT_SEC)in all three sweeps (per-anchor body factored into a localasync def, loop vars captured via default args).TimeoutError→logger.warning(run_id, anchor idx/coords) +errors_count+++ continue to the next anchor → the sweep finishes and reachesmark_done(no more 6h hang). The timeout branch sits before the existingexcept Exception, so per-anchor error handling is preserved.consecutive_failures, so the existingMAX_CONSECUTIVE_FAILURES=3abort fires on 3 consecutive hung anchors (the issue's optional "N timeouts → abort").CianScraperSERP session already hastimeout=30(cian.py:74) — no change needed;wait_foris the primary guard regardless.Test plan
test_anchor_watchdog.py), network-free: for each sweep, a mocked anchor coroutine that sleeps past a small timeout → asserts the sweep completes (no hang), hits the TimeoutError branch (counter incremented), proceeds to the next anchor, and reachesmark_done.pytest -k "sweep or pipeline or watchdog or anchor"→ 145 passed;ruffclean; no:x::type; AST ok.Refs #880, #581, #559
run_{avito,cian,yandex}_city_sweep had no per-anchor watchdog: a stalled HTTP request (proxy/connection hang) blocked the asyncio task forever — heartbeat froze mid-anchor, run stayed 'running' until the 6h zombie-detect. Live-repro: cian sweep #24 hung on anchor 3, #29 on anchor 1. Wrap each anchor's processing in asyncio.wait_for(coro, ANCHOR_TIMEOUT_SEC=240). TimeoutError -> logger.warning + errors_count++ + continue (sweep finishes, reaches mark_done). Timeout branch sits before the existing except Exception. yandex/cian also bump consecutive_failures -> existing MAX_CONSECUTIVE_FAILURES abort fires on 3 consecutive hung anchors. CianScraper SERP session already has timeout=30. 5 unit tests (all 3 sweeps), network-free. Refs #880, #581, #559🟠 FIX (только rebase) — код APPROVED, но PR
mergeable:false. main уехалee8f72d→d1034bb(другое окно тронулоscrape_pipeline.py) → конфликт.Код вычитан, корректен (пере-апрув будет быстрым):
asyncio.wait_for(anchor_coro, ANCHOR_TIMEOUT_SEC=240)во всех 3 sweep'ах; yandex/cian вынесли per-anchor фазы в локальныйasync defс правильным B023-mitigation (lat/lon/name через default-args; loop-invariant из замыкания — ок).except Exception→ errors++/consecutive++ → ban на 3 подряд → continue →mark_done(нет 6h-зависания). Семантика block/rate сохранена.wait_forcancel'ит только наawait(HTTP/sleep), не на syncdb.execute→ шаренная Session не рвётся mid-write, txn-corruption нет.ANCHOR_TIMEOUT_SECв__all__.→ rebase на текущий main → push. mergeable:true → мержу сразу (пере-ревью быстрый, код не трогай).
✅ APPROVE — снимаю прошлый FIX:
mergeable:falseбыл транзиентным (Forgejo не успел пересчитать mergeability сразу после сдвига main). Сейчасmergeable:trueпри том же SHAdc7a84c(worker не ребейзил — реального конфликта не было). Rebase не нужен.Код вычитан в прошлом ревью и корректен: per-anchor
asyncio.wait_for(240)во всех 3 sweep'ах, B023-safe innerasync def, TimeoutError→errors++/ban-on-3→continue→mark_done, sync-DB Session не рвётся (cancel только на await). Phase 3/4 relocate сохраняет #868/#867/#875. Мержу. #880 → qa.