feat(tradein/scheduler): cooperative drain coverage for sweeps/full-loads (phase 3a) #2074

Merged
lekss361 merged 1 commit from feat/tradein-sweep-drain-coverage into main 2026-06-28 18:04:02 +00:00
Owner

Зачем

Фаза 3a развязки scraper-деплоя (после #2065 ф.0+1, #2070 ф.2). Фаза 2 завела кооперативный SIGTERM-drain в avito_detail_backfill + rosreestr, но sweep'ы в scrape_pipeline.py его не honor'или (только is_cancelled) → на SIGTERM упирались в 80с-таймаут _drain_inflighthard-cancel на середине anchor'а. Этот PR заводит drain во все long-sweep чек-пойнты → они дренятся чисто (коммит текущего anchor'а, mark_done partial), а не убиваются.

Изменения (scrape_pipeline.py)

13 сайтов: к каждому существующему is_cancelled-чек-пойнту добавлен sibling elif shutdown_requested():update_heartbeat + mark_done(partial) + чистый стоп (без дальнейших anchor'ов / без IMV/detail-фазы). Покрыты: run_avito/cian/yandex_city_sweep, run_avito/cian/yandex_full_load (через существующий raise-sentinel RuntimeError("shutdown") из _on_bucket), run_avito_newbuilding_sweep, run_domclick_city_sweep.

  • Drain = mark_done(partial), НЕ mark_cancelled (user-cancel семантика byte-for-byte не тронута).
  • Без SIGTERM (shutdown_requested()==False) — поведение идентично текущему.
  • Дренированный run = done (reap_zombies не пометит зомби). Нет double-mark (проверено по каждому пути).

Review verdict

APPROVE (code-reviewer). drain≠cancel сверен с scrape_runs.py (is_cancelled==status уже 'cancelled' → mark_done no-op там; на SIGTERM status='running' → mark_done срабатывает). 3 judgment-call'а признаны корректными (domclick honest-status bypass byte-equivalent на pre-SERP; IMV-skip single-mark; sentinel "shutdown""cancelled", реальный RuntimeError не глотается). Инфо: domclick — единый citywide-вызов, mid-fetch drain'а нет → fallback на Phase-2 safety-net (структурно).

Тесты

tests/test_sweep_drain.py (new): return-style (avito/cian city_sweep) + sentinel-style (avito full_load) — shutdown после 1-й единицы → стоп на чек-пойнте, mark_done(partial), mark_cancelled НЕ вызван, дальнейшие anchor'ы/bucket'ы не обработаны. Локально: ruff-100 clean, 3 + -k "sweep or pipeline or cancel" 166 passed + регрессия 73.

Не входит: resume_cursor/paused (фаза 3b, отложено). Refs #1182.

## Зачем Фаза 3a развязки scraper-деплоя (после #2065 ф.0+1, #2070 ф.2). Фаза 2 завела кооперативный SIGTERM-drain в `avito_detail_backfill` + rosreestr, но **sweep'ы в `scrape_pipeline.py` его не honor'или** (только `is_cancelled`) → на SIGTERM упирались в 80с-таймаут `_drain_inflight` → **hard-cancel** на середине anchor'а. Этот PR заводит drain во все long-sweep чек-пойнты → они **дренятся чисто** (коммит текущего anchor'а, `mark_done` partial), а не убиваются. ## Изменения (`scrape_pipeline.py`) 13 сайтов: к каждому существующему `is_cancelled`-чек-пойнту добавлен sibling `elif shutdown_requested():` → `update_heartbeat` + `mark_done`(partial) + чистый стоп (без дальнейших anchor'ов / без IMV/detail-фазы). Покрыты: `run_avito/cian/yandex_city_sweep`, `run_avito/cian/yandex_full_load` (через существующий raise-sentinel `RuntimeError("shutdown")` из `_on_bucket`), `run_avito_newbuilding_sweep`, `run_domclick_city_sweep`. - Drain = `mark_done`(partial), **НЕ** `mark_cancelled` (user-cancel семантика byte-for-byte не тронута). - Без SIGTERM (`shutdown_requested()`==False) — поведение идентично текущему. - Дренированный run = `done` (reap_zombies не пометит зомби). Нет double-mark (проверено по каждому пути). ## Review verdict ✅ **APPROVE** (code-reviewer). `drain≠cancel` сверен с `scrape_runs.py` (`is_cancelled`==status уже 'cancelled' → mark_done no-op там; на SIGTERM status='running' → mark_done срабатывает). 3 judgment-call'а признаны корректными (domclick honest-status bypass byte-equivalent на pre-SERP; IMV-skip single-mark; sentinel `"shutdown"`≠`"cancelled"`, реальный RuntimeError не глотается). Инфо: domclick — единый citywide-вызов, mid-fetch drain'а нет → fallback на Phase-2 safety-net (структурно). ## Тесты `tests/test_sweep_drain.py` (new): return-style (avito/cian city_sweep) + sentinel-style (avito full_load) — shutdown после 1-й единицы → стоп на чек-пойнте, `mark_done`(partial), `mark_cancelled` НЕ вызван, дальнейшие anchor'ы/bucket'ы не обработаны. Локально: ruff-100 clean, 3 + `-k "sweep or pipeline or cancel"` **166 passed** + регрессия 73. Не входит: `resume_cursor`/`paused` (фаза 3b, отложено). Refs #1182.
lekss361 added 1 commit 2026-06-28 18:03:29 +00:00
feat(tradein/scraper): cooperative SIGTERM-drain coverage for sweep/full-load loops (#1182 Phase 3a)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
3320805723
Extend the cooperative shutdown-drain (Phase 2) to the long sweep and
full-load loops in scrape_pipeline.py so they stop CLEANLY on SIGTERM
instead of being hard-cancelled at the 80s _drain_inflight timeout.

At every between-unit checkpoint that already polls scrape_runs.is_cancelled
(user-cancel) we add an ALONGSIDE shutdown_requested() branch:
  - anchor-loop sweeps (avito/yandex/cian city) + avito newbuilding +
    domclick pre-SERP: update_heartbeat + mark_done(partial) + return;
  - full-load on_bucket callbacks (cian/yandex/avito): raise a distinct
    RuntimeError("shutdown") sentinel, handled next to the existing
    "cancelled" sentinel -> mark_done(partial), skipping the detail phase;
  - cian full-load detail loop: break -> existing post-loop mark_done.

Drain finalizes as mark_done(partial), NOT mark_cancelled (that stays
user-cancel only). is_cancelled -> user-cancel semantics are unchanged, and
behaviour is identical when shutdown_requested() is False. No resume_cursor
or paused status yet (Phase 3b, deferred): this is drain-coverage only.

Tests: tests/test_sweep_drain.py covers both structural variants -
run_avito_city_sweep / run_cian_city_sweep (anchor-loop) and
run_avito_full_load (on_bucket sentinel): shutdown flips True after the
first unit, asserting the sweep stops at the checkpoint, calls mark_done
with partial counters, never calls mark_cancelled, and does not process
later anchors/buckets/phases.
lekss361 merged commit efa2dc75ea into main 2026-06-28 18:04:02 +00:00
lekss361 deleted branch feat/tradein-sweep-drain-coverage 2026-06-28 18:04:02 +00:00
Sign in to join this conversation.
No reviewers
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#2074
No description provided.