gendesign/tradein-mvp/backend/app/tasks
bot-backend 848e6d3bdd feat(tradein/scraper): cooperative SIGTERM-drain for graceful deploy (#1182 Phase 2)
Деплой (docker recreate tradein-scraper) шлёт SIGTERM с stop_grace_period=120s
(Phase 1). Раньше scheduler_main отвечал hard task.cancel() → бегущий scrape-unit
получал CancelledError посреди браузер-карточки → карточка гибла без COMMIT.

Теперь SIGTERM/SIGINT лишь выставляют кооперативный флаг; tick-loop и длинные
задачи опрашивают его на СВОИХ существующих between-unit checkpoint'ах,
докоммичивают текущий unit и выходят сами.

- app/core/shutdown.py: новый standalone-модуль (asyncio.Event + request_shutdown
  / shutdown_requested / wait_for_shutdown), без app-зависимостей → нет циклов.
- scheduler_main._run: SIGTERM → request_shutdown() вместо task.cancel(); ждём
  добровольного drain'а, safety-net wait_for(100s < docker grace) с fallback на
  hard-cancel для некооперирующей задачи.
- scheduler_loop: проверка флага в начале тика и после каждого dispatch — не
  reap/claim новые run'ы во время shutdown, выходим из loop'а.
- avito_detail_backfill: break на границе карточки рядом с budget-guard; snapshot
  pending-query идемпотентен → следующий run сам резюмит остаток.
- import_rosreestr_dkp: расширен is_cancelled checkpoint — drain делает mark_done
  (partial), не mark_cancelled; user-cancel семантика без изменений.

Без SIGTERM поведение идентично прежнему. reap_zombies не трогает drained-run'ы
(они mark_done, не 'running').

Tests: tests/core/test_shutdown.py, scheduler_main drain+timeout-fallback,
avito_detail_backfill partial-drain. 26 + 29 scheduler passed.
2026-06-28 19:52:09 +03:00
..
__init__.py feat(tradein): search matview + indexes (Phase 3.1) (#469) 2026-05-23 14:01:18 +00:00
asking_to_sold_ratio.py fix(tradein/estimator): remove dead tier-aware-ratio path — truncation artifact + footgun (#2002) 2026-06-27 17:34:54 +00:00
avito_detail_backfill.py feat(tradein/scraper): cooperative SIGTERM-drain for graceful deploy (#1182 Phase 2) 2026-06-28 19:52:09 +03:00
backfill_listings_coords_geoportal.py feat(tradein): бэкфилл координат листингов из ekb_geoportal_buildings (street+house match) 2026-06-20 17:08:46 +03:00
cadastral_geo_match.py fix(cadastral): single-statement match UPDATE — OFFSET chunk skipped rows 2026-06-18 10:28:19 +03:00
cian_history_backfill.py feat(browser): per-source proxy pool behind FEATURE_BROWSER_POOL_ENABLED (Phase 1) 2026-06-18 09:01:50 +03:00
deactivate_stale_avito.py feat(scrapers): segment-aware generic deactivate_stale for yandex/cian (#1558) 2026-06-16 16:09:19 +00:00
ekb_geoportal_ingest.py feat(tradein): EKB geoportal building ingest + geocoder tier (complete city buildings) 2026-06-19 17:51:07 +03:00
gar_flats_load.py feat(tradein): канонический матч ГАР↔houses (0%→41% yield) + tradein_canon_addr + --match-only (#2063) 2026-06-28 15:50:07 +00:00
geocode_missing.py fix(geocode): set geocode_tried_at on successful geocode UPDATE 2026-06-16 10:07:51 +03:00
listing_source_snapshot.py feat(tradein): per-source listing price history — schema + daily snapshot (#570) (#646) 2026-05-29 12:18:04 +00:00
newbuilding_enrich_backfill.py feat(tradein-scheduler): nightly newbuilding-enrichment schedule (#973) 2026-06-13 16:24:03 +05:00
refresh_search_matview.py feat(tradein): premium_houses MV + premium_building flag (#2002) 2026-06-27 16:40:39 +00:00
rosreestr_quarter_poll.py feat(tradein): scheduled monthly Rosreestr new-quarter poll → ingest alert (#888) (#894) 2026-05-31 13:13:55 +00:00
sber_index_pull.py feat(tradein): СберИндекс monthly city-level price index pull (#887, data-layer) (#890) 2026-05-31 12:44:56 +00:00
yandex_address_backfill.py feat(tradein): schedule yandex address backfill task (#855) (#866) 2026-05-31 07:31:10 +00:00
yandex_detail_backfill.py fix(tradein): review minors — scheduler comment + save-error log label 2026-06-16 14:14:07 +03:00
yandex_newbuilding_sweep.py fix(tradein): yandex sweep — guard ext_id=NULL перед fetch_jk + cleanup httpx (#974 review M1/M3) 2026-06-15 18:41:08 +03:00