fix(scrapers): geocode-missing listings nightly scheduler — listings geom coverage backfill (all sources) #1549

Merged
lekss361 merged 2 commits from fix/geocode-missing-listings-scheduler into main 2026-06-16 08:32:23 +00:00
Owner

Проблема (verified на проде)

Покрытие geom по listings: avito 55%, yandex 82%, n1 41%, cian 99.8%. На свежих данных avito проседает до 10–18%.

Корневая причина: нет крона, геокодящего listings. На проде:

  • listings: 6959 avito + 590 yandex + 125 n1 объявлений имеют address, но geocode_tried_at IS NULLни разу не пробованы (последняя попытка 2026-05-23, разовый ручной прогон).
  • OS-crontab геокодит только deals (geocode_deals_from_houses, geocode_deals_nominatim) — никогда listings.
  • Скраперы сохраняют search-карточки с lat=lon=NULL в расчёте на «geocode-missing cron подтянет из address» (avito.py:419,685, n1.py:17, cian.py:749) — но такого крона для listings не существует.

Dry-run существующего batch-таска на проде: 85% успех (51/60 на топ-адресах) → подключение материально поднимет покрытие.

Фикс

Подключаем существующий address-dedup batch-backfill в in-app scheduler (repo-tracked, как yandex_address_backfill):

  • tasks/geocode_missing.py — (A) SELECT теперь учитывает geocode_tried_at (IS NULL OR < NOW()-7d) → loop-safe; на провале/исключении ставит geocode_tried_at=NOW() (7-дн backoff, failed-адреса не реселектятся); на успехе тоже ставит geocode_tried_at=NOW() (audit-контракт). (B) новый run_geocode_missing_listings(run_id, params) — batch-loop с wall-clock бюджетом + run-lifecycle (update_heartbeat/mark_done/mark_failed).
  • services/scheduler.pytrigger_geocode_missing_listings_run (зеркало yandex_address_backfill) + dispatch elif.
  • data/sql/110_scrape_schedules_seed_geocode_missing_listings.sql — seed-строка enabled=true, окно 6–9 UTC (после city-sweeps ~3–4 UTC и deals-геокода 5:30/5:45 UTC), batch_size=200, budget_sec=1800, idempotent ON CONFLICT DO NOTHING.
  • tests — обновлён tests/tasks/test_geocode_missing.py + новые: failure/exception marks tried_at, dry_run НЕ ставит tried_at, 7d-фильтр, все 3 break-условия loop'а.

Проверки

  • uv run pytest (полный suite, CI deselect): 1794 passed, 2 deselected.
  • ruff check + ruff format --check: clean.
  • code-reviewer: APPROVE (loop-termination доказана, без блокеров).
  • Затрагивает manual-эндпоинт /scrape/geocode-missing-listings (тот же geocode_missing_listings) — новый 7-дн backoff для него тоже желателен; dry_run не маркирует tried_at.

После мержа

Авто-применится миграция 110 (deploy strict). Первый прогон — завтра 06:00 UTC; покрытие avito ожидаемо растёт по мере очистки ~2000 distinct pending-адресов (~1 req/s Nominatim). Сверю coverage до/после.

## Проблема (verified на проде) Покрытие `geom` по listings: avito **55%**, yandex 82%, n1 41%, cian 99.8%. На свежих данных avito проседает до **10–18%**. Корневая причина: **нет крона, геокодящего listings**. На проде: - `listings`: 6959 avito + 590 yandex + 125 n1 объявлений имеют `address`, но `geocode_tried_at IS NULL` — **ни разу не пробованы** (последняя попытка 2026-05-23, разовый ручной прогон). - OS-crontab геокодит только `deals` (`geocode_deals_from_houses`, `geocode_deals_nominatim`) — **никогда listings**. - Скраперы сохраняют search-карточки с `lat=lon=NULL` в расчёте на «geocode-missing cron подтянет из address» (`avito.py:419,685`, `n1.py:17`, `cian.py:749`) — но такого крона для listings **не существует**. Dry-run существующего batch-таска на проде: **85% успех** (51/60 на топ-адресах) → подключение материально поднимет покрытие. ## Фикс Подключаем существующий address-dedup batch-backfill в in-app scheduler (repo-tracked, как `yandex_address_backfill`): - **`tasks/geocode_missing.py`** — (A) SELECT теперь учитывает `geocode_tried_at` (`IS NULL OR < NOW()-7d`) → loop-safe; на провале/исключении ставит `geocode_tried_at=NOW()` (7-дн backoff, failed-адреса не реселектятся); на успехе тоже ставит `geocode_tried_at=NOW()` (audit-контракт). (B) новый `run_geocode_missing_listings(run_id, params)` — batch-loop с wall-clock бюджетом + run-lifecycle (`update_heartbeat`/`mark_done`/`mark_failed`). - **`services/scheduler.py`** — `trigger_geocode_missing_listings_run` (зеркало `yandex_address_backfill`) + dispatch elif. - **`data/sql/110_scrape_schedules_seed_geocode_missing_listings.sql`** — seed-строка `enabled=true`, окно 6–9 UTC (после city-sweeps ~3–4 UTC и deals-геокода 5:30/5:45 UTC), `batch_size=200, budget_sec=1800`, idempotent `ON CONFLICT DO NOTHING`. - **tests** — обновлён `tests/tasks/test_geocode_missing.py` + новые: failure/exception marks tried_at, dry_run НЕ ставит tried_at, 7d-фильтр, все 3 break-условия loop'а. ## Проверки - `uv run pytest` (полный suite, CI deselect): **1794 passed, 2 deselected**. - `ruff check` + `ruff format --check`: clean. - code-reviewer: ✅ APPROVE (loop-termination доказана, без блокеров). - Затрагивает manual-эндпоинт `/scrape/geocode-missing-listings` (тот же `geocode_missing_listings`) — новый 7-дн backoff для него тоже желателен; dry_run не маркирует tried_at. ## После мержа Авто-применится миграция 110 (deploy strict). Первый прогон — завтра 06:00 UTC; покрытие avito ожидаемо растёт по мере очистки ~2000 distinct pending-адресов (~1 req/s Nominatim). Сверю coverage до/после.
lekss361 added 2 commits 2026-06-16 07:08:33 +00:00
fix(tradein): wire nightly geocode backfill для listings в in-app scheduler
All checks were successful
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / openapi-codegen-check (push) Has been skipped
a56420bdfb
Root cause: 7674 avito/yandex/n1 listings з null-geom мають ненульовий address,
але geocode_tried_at IS NULL — OS cron геокодує тільки deals, ніколи listings.

Parts:
- geocode_missing.py: WHERE додає geocode_tried_at IS NULL OR tried_at < 7d
  (loop-safe); при failure (geo=None / exception) позначаємо tried_at=NOW().
  Новий run_geocode_missing_listings: batch loop з wall-clock budget (30m),
  update_heartbeat / mark_done / mark_failed lifecycle через scrape_runs.
- scheduler.py: trigger_geocode_missing_listings_run + dispatch elif.
- migration 110: seed scrape_schedules row (enabled=true, window 06-09 UTC).
- tests: оновлено 2 тести (commit на tried_at UPDATE); +7 нових тестів.
  1794 passed.
fix(geocode): set geocode_tried_at on successful geocode UPDATE
All checks were successful
CI / changes (push) Successful in 8s
CI / backend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (push) Has been skipped
CI / openapi-codegen-check (push) 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
17fa6544a2
Add geocode_tried_at = NOW() to the success UPDATE SET clause so
successfully-geocoded listings record their attempt time, matching
the per-ID admin endpoint contract and the column spec.
lekss361 added the
bug
scope/backend
scrapers
labels 2026-06-16 07:08:40 +00:00
lekss361 merged commit b968b811cb into main 2026-06-16 08:32:23 +00:00
lekss361 deleted branch fix/geocode-missing-listings-scheduler 2026-06-16 08:32:23 +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#1549
No description provided.