-- 139_seed_domclick_detail_backfill.sql -- Seed row для DomClick Layer B detail-backfill — периодическое обогащение карточек. -- -- !!! DORMANT BY DESIGN !!! enabled = false. -- Layer A (domclick_city_sweep, #1979) собирает только базовые SERP-поля. Layer B -- дочитывает страницу карточки (.../card/sale__flat__) и обогащает listings: -- renovation/repair_state, living/kitchen area, balconies, sale_type, owners_count, -- encumbrances, year_built, views, priceHistory. -- -- Транспорт — ТОЛЬКО BrowserFetcher(source="domclick"): карточки за DataDome, -- curl_cffi отдаёт 401 (нет curl-fallback). Оркестратор открывает ОДНУ браузер-сессию -- на весь прогон. Block (DataDome / fetch-fail) транзиентен → abort после -- max_consecutive_blocks с mark_done (НЕ mark_failed); retry следующей ночью через -- detail_enriched_at IS NULL. Parse-failure (HTTP 200, но SSR не парсится) → листинг -- остаётся необогащённым (rollback) и переотбирается на следующем прогоне. -- -- Capability полностью wired (run_domclick_detail_backfill + trigger_domclick_detail_ -- backfill_run + dispatch в scheduler.py), но schedule засеян ВЫКЛЮЧЕННЫМ — live-форма -- SSR-стейта и поведение DataDome НЕ verified до ручного prod-smoke. ВКЛЮЧАТЬ ВРУЧНУЮ: -- UPDATE scrape_schedules SET enabled = true WHERE source = 'domclick_detail_backfill'; -- Зеркалит yandex_detail_backfill (113) / avito_detail_backfill (112) и dormant-сиды -- domclick_city_sweep (122/138). -- -- ЗАВИСИМОСТИ: -- - 052_scrape_schedules.sql (таблица + UNIQUE(source)). -- - Layer A (#1979 / domclick_city_sweep) должен СНАЧАЛА наполнить listings -- source='domklik' (иначе snapshot пуст → no-op mark_done на каждом прогоне). -- Idempotent: ON CONFLICT (source) DO NOTHING — безопасно запускать повторно. -- -- Окно 15:00-18:00 UTC (18:00-21:00 МСК) — после avito (09-12) / yandex (12-15) detail -- backfills и city sweeps, чтобы не конкурировать за egress на shared mobile proxy. -- default_params (консервативно, под DataDome — browser-only транспорт медленный): -- batch_size — размер snapshot (SELECT LIMIT) на один прогон. -- budget_sec — wall-clock бюджет прогона (сек). -- request_delay_sec — пауза между карточками (anti-DataDome). -- max_consecutive_blocks — порог последовательных блоков до abort. BEGIN; -- next_run_at = следующее наступление окна (tomorrow + window_start_hour=15h). -- При enabled=false get_due_schedules() строку не подхватит, но заполняем по образцу -- 122/138: когда оператор включит schedule, next_run_at уже валиден и run не выстрелит -- мгновенно (вне окна). AT TIME ZONE 'UTC' фиксирует wall-clock час в UTC независимо -- от session TimeZone GUC. INSERT INTO scrape_schedules ( source, enabled, window_start_hour, window_end_hour, next_run_at, default_params ) VALUES ( 'domclick_detail_backfill', false, -- DORMANT — enable manually after prod-smoke 15, 18, ((CURRENT_DATE + INTERVAL '1 day') + make_interval(hours => 15)) AT TIME ZONE 'UTC', '{"batch_size": 300, "budget_sec": 3600, "request_delay_sec": 8, "max_consecutive_blocks": 5}'::jsonb ) ON CONFLICT (source) DO NOTHING; COMMIT;