feat(tradein): checkpoint/resume for yandex full-load — survive restart (#930) #937

Merged
bot-reviewer merged 1 commit from feat/yandex-fullload-checkpoint-resume into main 2026-05-31 21:34:24 +00:00
Owner

Follow-up к #931. Checkpoint/resume-коммит был запушен в ветку #931 после того как reviewer уже squash-смерджил #931 (только exhaustive-коммит попал в main) — squash-merge timing trap. Этот PR доносит orphaned checkpoint-работу (cherry-pick 3bcfb6bd45018f поверх текущего main). Зеркало Cian #932.

Summary

  • yandex_realty.pyfetch_all_secondary/_walk_price_range принимают skip_buckets: set[str] | None. bucket_key = "{rooms}:{lo}:{hi}". Skip пропускает только пагинацию + on_bucket (probe/split-решение остаются). Применено к обоим leaf-путям Yandex: нормальному параллельному и total is None paginate-until-empty fallback. Callback → on_bucket(bucket_key, lots).
  • scrape_pipeline.pyrun_yandex_full_load(resume_run_id: int | None = None). При resume читает done_buckets из counters прошлого run (SELECT counters FROM scrape_runs WHERE id = CAST(:rid AS bigint)), сидит skip-set. _on_bucket(bucket_key, lots) дописывает в done + heartbeat {**counters, "done_buckets": sorted(done)} (включая пустой-lots бранч). mark_done в обоих ветках (success + cancelled) включает done_buckets → resumed-run сам резюмируем. mark_failed — без.
  • admin.pyYandexFullLoadRequest.resume_run_id: int | None, проброшен в run_yandex_full_load. Без него — full walk с нуля.

Test plan

  • ruff check (3 файла) → All checks passed
  • pytest -k "yandex or full_load"306 passed (на актуальном main-базисе после cherry-pick, не доверяя прошлому прогону — перепрогнал сам)
  • Cherry-pick чистый (auto-merge admin.py + scrape_pipeline.py, без конфликтов)
  • 2 новых теста: test_skip_buckets_skips_pagination_and_on_bucket, test_on_bucket_receives_key_and_lots + обновлены existing on_bucket-стабы под 2-арг сигнатуру.

Refs #930 (Yandex-половина; Cian-половина уже в main = #932). Не запускалось против prod.

Follow-up к #931. Checkpoint/resume-коммит был запушен в ветку #931 **после** того как reviewer уже squash-смерджил #931 (только exhaustive-коммит попал в main) — squash-merge timing trap. Этот PR доносит orphaned checkpoint-работу (cherry-pick `3bcfb6b` → `d45018f` поверх текущего main). Зеркало Cian #932. ## Summary - `yandex_realty.py` — `fetch_all_secondary`/`_walk_price_range` принимают `skip_buckets: set[str] | None`. `bucket_key = "{rooms}:{lo}:{hi}"`. Skip пропускает только пагинацию + on_bucket (probe/split-решение остаются). Применено к **обоим** leaf-путям Yandex: нормальному параллельному и `total is None` paginate-until-empty fallback. Callback → `on_bucket(bucket_key, lots)`. - `scrape_pipeline.py` — `run_yandex_full_load(resume_run_id: int | None = None)`. При resume читает `done_buckets` из counters прошлого run (`SELECT counters FROM scrape_runs WHERE id = CAST(:rid AS bigint)`), сидит skip-set. `_on_bucket(bucket_key, lots)` дописывает в `done` + heartbeat `{**counters, "done_buckets": sorted(done)}` (включая пустой-lots бранч). `mark_done` в **обоих** ветках (success + cancelled) включает `done_buckets` → resumed-run сам резюмируем. `mark_failed` — без. - `admin.py` — `YandexFullLoadRequest.resume_run_id: int | None`, проброшен в `run_yandex_full_load`. Без него — full walk с нуля. ## Test plan - [x] `ruff check` (3 файла) → All checks passed - [x] `pytest -k "yandex or full_load"` → **306 passed** (на актуальном main-базисе после cherry-pick, не доверяя прошлому прогону — перепрогнал сам) - [x] Cherry-pick чистый (auto-merge admin.py + scrape_pipeline.py, без конфликтов) - 2 новых теста: `test_skip_buckets_skips_pagination_and_on_bucket`, `test_on_bucket_receives_key_and_lots` + обновлены existing on_bucket-стабы под 2-арг сигнатуру. Refs #930 (Yandex-половина; Cian-половина уже в main = #932). Не запускалось против prod.
lekss361 added 1 commit 2026-05-31 21:30:09 +00:00
lekss361 added the
scope/backend
scrapers
labels 2026-05-31 21:30:16 +00:00
bot-reviewer approved these changes 2026-05-31 21:34:08 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — faithful mirror одобренного #932 на yandex, sig-change консистентен. Мержу.

Verified (урок #919 — cross-callsite sig consistency)

  • on_bucket 1-arg→2-arg (bucket_key, lots) изменён во всех yandex call-site'ах: оба leaf-пути (normal parallel + total=None fallback) + run_yandex_full_load._on_bucket. Test-стабы обновлены на 2-arg → нет осиротевших 1-arg вызовов (автор перепрогнал 306 passed на текущем main).
  • skip_buckets проброшен fetch_all_secondary_walk_price_range (обе ветки рекурсии + оба leaf-пути); skip-check post-probe/pre-pagination в обоих leaf'ах. bucket_key = "{rooms}:{lo}:{hi}".
  • resume_run_id читает counters через CAST(:rid AS bigint) (psycopg-v3); done копится; mark_done несёт done_buckets в success+cancel ветках.
  • Cross-file: тронуты только yandex_realty/scrape_pipeline(run_yandex_full_load)/admin — cian _on_bucket (уже 2-arg из #932) не задет, коллизии нет.
  • Squash-timing-trap объяснён честно (checkpoint-коммит осиротел когда #931 сквошнулся раньше); cherry-pick чистый на актуальный main.

🟡 Advisory (как в #932)

bucket_key совпадёт на resume только если split-дерево идентично между прогонами (totalItems/fallback дрейфует) — mismatched бакеты re-paginate, dedup гасит. Best-effort.

Squash-merge + delete branch. Закрывает Yandex-половину #930 (Cian = #932 в main).

<!-- gendesign-review-bot: sha=d45018f verdict=approve --> ✅ **APPROVE** — faithful mirror одобренного #932 на yandex, sig-change консистентен. Мержу. ## Verified (урок #919 — cross-callsite sig consistency) - `on_bucket` 1-arg→2-arg `(bucket_key, lots)` изменён **во всех** yandex call-site'ах: оба leaf-пути (normal parallel + `total=None` fallback) + `run_yandex_full_load._on_bucket`. Test-стабы обновлены на 2-arg → нет осиротевших 1-arg вызовов (автор перепрогнал 306 passed на текущем main). - `skip_buckets` проброшен `fetch_all_secondary`→`_walk_price_range` (обе ветки рекурсии + оба leaf-пути); skip-check post-probe/pre-pagination в обоих leaf'ах. `bucket_key = "{rooms}:{lo}:{hi}"`. - `resume_run_id` читает `counters` через `CAST(:rid AS bigint)` (psycopg-v3); `done` копится; `mark_done` несёт `done_buckets` в success+cancel ветках. - **Cross-file**: тронуты только yandex_realty/scrape_pipeline(run_yandex_full_load)/admin — cian `_on_bucket` (уже 2-arg из #932) не задет, коллизии нет. - Squash-timing-trap объяснён честно (checkpoint-коммит осиротел когда #931 сквошнулся раньше); cherry-pick чистый на актуальный main. ## 🟡 Advisory (как в #932) `bucket_key` совпадёт на resume только если split-дерево идентично между прогонами (totalItems/fallback дрейфует) — mismatched бакеты re-paginate, dedup гасит. Best-effort. Squash-merge + delete branch. Закрывает Yandex-половину #930 (Cian = #932 в main).
bot-reviewer merged commit d93e96fb0c into main 2026-05-31 21:34:24 +00:00
bot-reviewer deleted branch feat/yandex-fullload-checkpoint-resume 2026-05-31 21:34:24 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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#937
No description provided.