refactor(tradein/scheduler): удалить legacy scheduler_loop, kit единственный путь (#2397 Part C) #2402

Merged
lekss361 merged 2 commits from refactor/tradein-remove-legacy-scheduler-loop into main 2026-07-04 10:04:16 +00:00
Owner

Summary

Umbrella #2397 Part C (эпик #2277 decommission legacy orchestration). Удаляет legacy in-app scraper-scheduler (scheduler_loop + 27 trigger_*_run + dispatch-машинерия) из app/services/scheduler.py (2098→418 строк), делая scrape_pipeline.py удаляемым в Part E.

Топология подтверждена перед удалением (docker-compose.prod.yml): tradein-backend SCHEDULER_ENABLE=false (main.py in-app launch был мёртв), tradein-scraper SCHEDULER_ENABLE=true+USE_KIT_SCHEDULER=true (уже 100% kit). _run_kit_scheduler самодостаточен, не импортит legacy. Все 29 job-sources имеют kit-handler (deep-review вручную сверил).

  • scheduler.py: удалено scheduler_loop, get_due_schedules, reap_zombies, _claim_run, _defer_next_run_at, _spawn_tracked/_drain_inflight, 27 trigger_*_run, импорт scrape_pipeline. Оставлено: compute_next_run_at, has_running_run (admin.py), import_rosreestr_dkp, _execute_cian_backfill (product_handlers.py).
  • main.py: убран launch legacy scheduler_loop (dead-code, backend SCHEDULER_ENABLE=false).
  • scheduler_main.py: убрана ship-dark развилка USE_KIT_SCHEDULER=false → legacy fallback — kit теперь безусловный путь. SIGTERM/drain-семантика сохранена. (Поле флага оставлено, extra="ignore" защищает от leftover env.)
  • app/tasks docstrings: обновлены ссылки trigger_X_runproduct_handlers._job_X (docstring-only).
  • scrape_pipeline.py: 0 рантайм-импортёров после этого PR (orphaned, готов к Part E). Файл не тронут.
  • Тесты: удалены/сжаты тесты удалённой машинерии; добавлен нетавтологичный registry-completeness гвард (гоняет реальный build_product_handlers, ловит будущий уронённый handler — доказано break/restore), починен устаревший _PRODUCT_SOURCES (+cian_history_backfill/deals_freshness_monitor/osm_poi_ekb_refresh).

⚠️ Behavior change

Убирает fallback USE_KIT_SCHEDULER=false — kit единственный scheduler-путь. Интенционально (директива «сноси»). Прод уже на kit; откат = git revert (не флаг). Dev-compose: backend больше не запускает scheduler в dev (был legacy, dev-only, не прод).

Review

deep-code-reviewer APPROVE. 3 критичных проверки прошли: нет пропущенного живого вызывающего, SIGTERM-drain сохранён, все job-sources имеют kit-handler. Единственный 🟠 (потерянный registry-гвард) закрыт в этом же PR.

Test plan

  • Full suite: 3179 passed, 6 skipped, 1 pre-existing unrelated fail (#2208)
  • import app.main; import app.scheduler_main — OK
  • scheduler-тесты (parity/scheduler/scheduler_main): 33 passed; completeness-гвард ловит регресс (break/restore)
  • ruff check clean (pinned 0.7.4); grep — 0 рантайм-импортёров удалённых функций

Post-merge: qa-smoke на tradein-scraper — kit-scheduler тикает + джоба фаерится.

Refs #2397, #2277

## Summary Umbrella #2397 Part C (эпик #2277 decommission legacy orchestration). Удаляет legacy in-app scraper-scheduler (`scheduler_loop` + 27 `trigger_*_run` + dispatch-машинерия) из `app/services/scheduler.py` (2098→418 строк), делая `scrape_pipeline.py` удаляемым в Part E. **Топология подтверждена перед удалением** (`docker-compose.prod.yml`): `tradein-backend` `SCHEDULER_ENABLE=false` (main.py in-app launch был мёртв), `tradein-scraper` `SCHEDULER_ENABLE=true`+`USE_KIT_SCHEDULER=true` (уже 100% kit). `_run_kit_scheduler` самодостаточен, не импортит legacy. Все 29 job-sources имеют kit-handler (deep-review вручную сверил). - `scheduler.py`: удалено `scheduler_loop`, `get_due_schedules`, `reap_zombies`, `_claim_run`, `_defer_next_run_at`, `_spawn_tracked/_drain_inflight`, 27 `trigger_*_run`, импорт scrape_pipeline. Оставлено: `compute_next_run_at`, `has_running_run` (admin.py), `import_rosreestr_dkp`, `_execute_cian_backfill` (product_handlers.py). - `main.py`: убран launch legacy `scheduler_loop` (dead-code, backend SCHEDULER_ENABLE=false). - `scheduler_main.py`: **убрана ship-dark развилка `USE_KIT_SCHEDULER=false` → legacy fallback** — kit теперь безусловный путь. SIGTERM/drain-семантика сохранена. (Поле флага оставлено, `extra="ignore"` защищает от leftover env.) - app/tasks docstrings: обновлены ссылки `trigger_X_run` → `product_handlers._job_X` (docstring-only). - `scrape_pipeline.py`: **0 рантайм-импортёров** после этого PR (orphaned, готов к Part E). Файл не тронут. - Тесты: удалены/сжаты тесты удалённой машинерии; **добавлен нетавтологичный registry-completeness гвард** (гоняет реальный `build_product_handlers`, ловит будущий уронённый handler — доказано break/restore), починен устаревший `_PRODUCT_SOURCES` (+cian_history_backfill/deals_freshness_monitor/osm_poi_ekb_refresh). ## ⚠️ Behavior change Убирает fallback `USE_KIT_SCHEDULER=false` — kit единственный scheduler-путь. Интенционально (директива «сноси»). Прод уже на kit; откат = `git revert` (не флаг). Dev-compose: backend больше не запускает scheduler в dev (был legacy, dev-only, не прод). ## Review `deep-code-reviewer` — ✅ APPROVE. 3 критичных проверки прошли: нет пропущенного живого вызывающего, SIGTERM-drain сохранён, все job-sources имеют kit-handler. Единственный 🟠 (потерянный registry-гвард) закрыт в этом же PR. ## Test plan - [x] Full suite: 3179 passed, 6 skipped, 1 pre-existing unrelated fail (#2208) - [x] `import app.main; import app.scheduler_main` — OK - [x] scheduler-тесты (parity/scheduler/scheduler_main): 33 passed; completeness-гвард ловит регресс (break/restore) - [x] `ruff check` clean (pinned 0.7.4); grep — 0 рантайм-импортёров удалённых функций Post-merge: qa-smoke на `tradein-scraper` — kit-scheduler тикает + джоба фаерится. Refs #2397, #2277
lekss361 added 2 commits 2026-07-04 10:01:31 +00:00
Топология подтверждена перед удалением (docker-compose.prod.yml): tradein-backend
(uvicorn app.main:app) — SCHEDULER_ENABLE=false; tradein-scraper (python -m
app.scheduler_main) — SCHEDULER_ENABLE=true + USE_KIT_SCHEDULER=true. Kit-путь
(_run_kit_scheduler → scraper_kit.orchestration.scheduler + product_handlers)
самодостаточен: не импортирует ничего из app.services.scheduler.scheduler_loop
или app.services.scrape_pipeline. Все НЕ-sweep джобы, которые kit-scheduler
диспетчерит через build_product_handlers, идут напрямую в app.tasks.*/
app.services.* (либо lazy-импортят import_rosreestr_dkp/_execute_cian_backfill
из scheduler.py) — мимо удаляемой legacy-машинерии.

app/services/scheduler.py: 2098 → 418 строк. Удалено: scheduler_loop,
get_due_schedules, reap_zombies, _claim_run, _defer_next_run_at, _spawn_tracked/
_drain_inflight/_inflight_tasks, все 27 trigger_*_run-функций, импорт
app.services.scrape_pipeline, константы SCHEDULER_TICK_SEC/ZOMBIE_THRESHOLD_HOURS
(достижимы были только через удалённый scheduler_loop-путь). Оставлено (живые
импортёры вне удалённого): compute_next_run_at + has_running_run (admin.py),
import_rosreestr_dkp + _execute_cian_backfill (lazy-импорты в
product_handlers.py — job-тела kit-handler'ов).

main.py: убран `from app.services.scheduler import scheduler_loop` + lifespan-блок
запуска (`if settings.scheduler_enable: asyncio.create_task(scheduler_loop())`);
прод-backend всегда шёл с SCHEDULER_ENABLE=false, так что это был мёртвый код.

scheduler_main.py: убрана ship-dark развилка #2192 (USE_KIT_SCHEDULER=false →
legacy scheduler_loop fallback) — _run_kit_scheduler() теперь безусловный путь.
Поле settings.use_kit_scheduler оставлено в конфиге (Settings extra="ignore"
защищает от startup-краха на leftover env var), но на ветвление не влияет.

app.services.scrape_pipeline: 0 runtime-импортёров в app/+scripts/+packages/
после этого PR (только тесты, которые Part E удалит вместе с самим файлом) —
подтверждено grep. scrape_pipeline.py не тронут (Part E).

Тесты: удалены test_house_imv_backfill_scheduler.py (100% legacy-триггер,
backfill_house_imv сервис покрыт в test_house_imv_backfill_browser_flag.py /
test_backfill_wave2.py) и test_kit_registry_completeness.py (parity-инвариант
против удалённого dispatch, дублирует test_scraper_kit_scheduler_parity.py).
Точечно вырезаны "Scheduler wiring" секции (trigger_fn_exists/dispatch_branch_
wired/runs_in_executor) из ~10 файлов, тестирующих сами task-функции — сами
task-тесты (SQL-shape, миграции, fake-db поведение) оставлены нетронутыми.
test_scheduler.py: 825 → ~90 строк (остались только compute_next_run_at-тесты).
test_scraper_kit_scheduler_parity.py: убрана golden-parity секция против
удалённого scheduler_loop (SOURCE_TO_OLD_TRIGGER/_drive_old_one_tick/
test_routing_parity_per_source), остальное (claim/reap_zombies/dispatch/
registry-shape тесты kit-модуля) сохранено — источник этих инвариантов не
app.services.scheduler, а сам scraper_kit.orchestration.scheduler.
test_scheduler_main.py: 2 теста, патчившие app.services.scheduler.scheduler_loop,
переведены на монкипатч sm._run_kit_scheduler (единственный путь после этого PR).
test_sweep_imv_phase.py:171-371 (6 прямых импортов run_avito_city_sweep из
scrape_pipeline) намеренно НЕ тронуты — Part E.

Verify: полный pytest 3179 passed / 6 skipped / 1 known-unrelated fail
(test_search_cache_hit, #2208, не связан с этим PR); ruff 0.7.4 чист на всех
изменённых файлах; `python -c "import app.main; import app.scheduler_main"` OK.
test(tradein/scheduler): нетавтологичный kit registry-completeness guard (#2397 Part C)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 14s
CI / changes (pull_request) Successful in 14s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m57s
c30581ae8d
Deep-review нашёл: удалённый test_kit_registry_completeness.py заменялся
test_routing_coverage_sets_match, но тот строил registry ИЗ хардкоженного
_PRODUCT_SOURCES через recording-stub и проверял тот же набор — тавтология,
не гоняющая реальный build_product_handlers(). Плюс _PRODUCT_SOURCES был
устаревшим: не хватало cian_history_backfill, deals_freshness_monitor,
osm_poi_ekb_refresh (все три есть в реальном build_product_handlers).

Фикс:
- _PRODUCT_SOURCES дополнен тремя пропущенными source'ами, сверен построчным
  grep '"...": Handler(' по product_handlers.py (20 exact + 1 wildcard).
  Список остаётся canonical hardcode (источник правды — сам product_handlers.py
  + scrape_schedules seed-миграции), с явным комментарием "поддерживается вручную".
- test_routing_coverage_sets_match удалён, заменён
  test_real_build_product_handlers_covers_all_scheduled_sources: зовёт РЕАЛЬНЫЙ
  product_handlers.build_product_handlers(ctx=None) + build_registry (не stub),
  ассертит resolve_handler для каждого source из _PRODUCT_SOURCES ∪
  _KIT_NATIVE_SOURCES. Уроненная handler-entry или scheduled source без
  handler'а теперь роняет CI.

Регресс-пруф (сделан вручную, откачен): временно закомментировал
"osm_poi_ekb_refresh" в build_product_handlers → новый тест упал с
"misses source=osm_poi_ekb_refresh"; вернул строку → тест снова зелёный
(git diff на product_handlers.py пуст после отката).

Verify: full pytest 3179 passed / 6 skipped / 1 known-unrelated fail
(test_search_cache_hit, #2208); ruff 0.7.4 чист на изменённых файлах.
lekss361 merged commit 828d691c0d into main 2026-07-04 10:04:16 +00:00
lekss361 deleted branch refactor/tradein-remove-legacy-scheduler-loop 2026-07-04 10:04:16 +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#2402
No description provided.