tradein/scheduler: пропуск по протухшим кукам Циана отбрасывает следующий запуск на сутки вместо шести часов — _defer_next_run_at не знает про interval_minutes #3312

Closed
opened 2026-09-01 07:28:00 +00:00 by lekss361 · 0 comments
Owner

Дефект

cian_detail_backfill работает с каденцией 360 минут, но у него же стоит pre_claim, и на пути пропуска каденция теряется: следующий запуск назначается на случайный час завтрашних суток.

Цепочка проверена по коду построчно:

1. backend/app/services/product_handlers.py:726-731 — у источника заданы обе ручки:

"cian_detail_backfill": Handler(
    _job_cian_history_backfill,
    "cian_detail_backfill",
    pre_claim=_cian_pre_claim,
    post_claim=reschedule_after_minutes(param="interval_minutes", default=360),
),

2. _cian_pre_claim при негодных куках делает early-return, и обе его ветки пропуска зовут kit_defer_next_run_atproduct_handlers.py:116 (кук нет / протухли / помечены невалидными) и :127 (Циан не принимает куки, разлогин).

3. packages/scraper-kit/src/scraper_kit/orchestration/scheduler.py:692-705 — этот defer читает только interval_days:

next_at = compute_next_run_at(
    schedule_row["window_start_hour"],
    schedule_row["window_end_hour"],
    interval_days=int(params.get("interval_days", 1)),
)

4. compute_next_run_at (scheduler.py:317-342) берёт случайное время внутри окна через interval_days суток: target = (now + timedelta(days=interval_days)).date().

В default_params у cian_detail_backfill лежит {"batch_size": 400, "interval_minutes": 360, "listings_pending": "detail", "do_houses": false} — ключа interval_days там нет, значит 1. Окно источника 0..23, поэтому «завтра в случайный час» даёт задержку от ~1 до ~47 часов, в среднем сутки — вместо шести.

post_claim, который и знает про interval_minutes, на путь пропуска не попадает по построению: он срабатывает только ПОСЛЕ claim'а.

Почему это дороже, чем кажется

Цена не в самом простое сбора — при негодных куках Циан не соберётся всё равно. Цена в задержке возобновления: куки Циана обновляются вручную, и после обновления сбор должен подхватиться в пределах шести часов, а подхватится через сутки-двое. То есть ручная починка не даёт эффекта ещё сутки, и это выглядит как «обновил куки, а оно не работает».

Циан — самый недобранный источник: живая очередь 15 463 карточки при покрытии 22.7%, худшее из четырёх площадок. Каждые потерянные сутки на каденции 6 ч — это четыре несостоявшихся прогона по 400 карточек.

Состояние: ещё не выстрелило

В scrape_runs у cian_detail_backfill пока нет ни одной строки со статусом skipped — расписание заведено 30.08, куки за это время не протухали. Дефект латентный, но срабатывание неизбежно: механизм существует ровно для случая протухших кук.

Что делать

Научить _defer_next_run_at читать interval_minutes так же, как это делает reschedule_after_minutes, с прежним падением на interval_days когда минутного ключа нет. Правка в scraper-kit, поэтому затрагивает все источники с pre_claim — проверить, что у остальных поведение не меняется (у кого нет interval_minutes, всё остаётся как было).

Осторожно: у _defer_next_run_at есть вторая задача помимо каденции — не давать get_due_schedules переотбирать расписание на каждом тике (#1522). Минутный интервал не должен опускаться до значений, при которых pre-check снова начнёт гоняться раз в минуту.

Приёмка

  • Пропуск по куке у источника с interval_minutes двигает next_run_at на этот интервал, а не на сутки
  • У источника без interval_minutes поведение прежнее (тест на неизменность)
  • Защита из #1522 сохранена: pre-check не гоняется каждый тик
  • Проверено на cian_detail_backfill: после пропуска следующий запуск через ~6 ч

Refs #3284, #1522, #3301

## Дефект `cian_detail_backfill` работает с каденцией **360 минут**, но у него же стоит `pre_claim`, и на пути пропуска каденция теряется: следующий запуск назначается на **случайный час завтрашних суток**. Цепочка проверена по коду построчно: **1.** `backend/app/services/product_handlers.py:726-731` — у источника заданы обе ручки: ```python "cian_detail_backfill": Handler( _job_cian_history_backfill, "cian_detail_backfill", pre_claim=_cian_pre_claim, post_claim=reschedule_after_minutes(param="interval_minutes", default=360), ), ``` **2.** `_cian_pre_claim` при негодных куках делает early-return, и **обе** его ветки пропуска зовут `kit_defer_next_run_at` — `product_handlers.py:116` (кук нет / протухли / помечены невалидными) и `:127` (Циан не принимает куки, разлогин). **3.** `packages/scraper-kit/src/scraper_kit/orchestration/scheduler.py:692-705` — этот defer читает **только** `interval_days`: ```python next_at = compute_next_run_at( schedule_row["window_start_hour"], schedule_row["window_end_hour"], interval_days=int(params.get("interval_days", 1)), ) ``` **4.** `compute_next_run_at` (`scheduler.py:317-342`) берёт **случайное время внутри окна через `interval_days` суток**: `target = (now + timedelta(days=interval_days)).date()`. В `default_params` у `cian_detail_backfill` лежит `{"batch_size": 400, "interval_minutes": 360, "listings_pending": "detail", "do_houses": false}` — ключа `interval_days` там **нет**, значит `1`. Окно источника `0..23`, поэтому «завтра в случайный час» даёт задержку **от ~1 до ~47 часов, в среднем сутки** — вместо шести. `post_claim`, который и знает про `interval_minutes`, на путь пропуска не попадает по построению: он срабатывает только ПОСЛЕ claim'а. ## Почему это дороже, чем кажется Цена не в самом простое сбора — при негодных куках Циан не соберётся всё равно. Цена в **задержке возобновления**: куки Циана обновляются вручную, и после обновления сбор должен подхватиться в пределах шести часов, а подхватится через сутки-двое. То есть ручная починка не даёт эффекта ещё сутки, и это выглядит как «обновил куки, а оно не работает». Циан — самый недобранный источник: живая очередь **15 463** карточки при покрытии 22.7%, худшее из четырёх площадок. Каждые потерянные сутки на каденции 6 ч — это четыре несостоявшихся прогона по 400 карточек. ## Состояние: ещё не выстрелило В `scrape_runs` у `cian_detail_backfill` пока **нет ни одной строки со статусом `skipped`** — расписание заведено 30.08, куки за это время не протухали. Дефект латентный, но срабатывание неизбежно: механизм существует ровно для случая протухших кук. ## Что делать Научить `_defer_next_run_at` читать `interval_minutes` так же, как это делает `reschedule_after_minutes`, с прежним падением на `interval_days` когда минутного ключа нет. Правка в scraper-kit, поэтому затрагивает все источники с `pre_claim` — проверить, что у остальных поведение не меняется (у кого нет `interval_minutes`, всё остаётся как было). Осторожно: у `_defer_next_run_at` есть вторая задача помимо каденции — не давать `get_due_schedules` переотбирать расписание на каждом тике (#1522). Минутный интервал не должен опускаться до значений, при которых pre-check снова начнёт гоняться раз в минуту. ## Приёмка - [ ] Пропуск по куке у источника с `interval_minutes` двигает `next_run_at` на этот интервал, а не на сутки - [ ] У источника без `interval_minutes` поведение прежнее (тест на неизменность) - [ ] Защита из #1522 сохранена: pre-check не гоняется каждый тик - [ ] Проверено на `cian_detail_backfill`: после пропуска следующий запуск через ~6 ч Refs #3284, #1522, #3301
Sign in to join this conversation.
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#3312
No description provided.