-- 212_sber_index_pull_weekly.sql -- sber_index_pull: такт 28 дней → 7. Ревью PR #2681 (#2674). -- -- ПОЧЕМУ. Монитор sber_freshness_monitor алертил при age > 60д -- (sber_index_max_age_days 35 + lag_allowance 25). Прод-разбор всех 24 прогонов -- монитора (read-only, 2026-08-06, scrape_runs.counters) показал ПИЛУ, а не застой: -- -- 13-16.07 alert=1 age 73,74,75,76 latest_month=5 (май) -- 17.07 alert=0 age 46 latest_month=6 ← день загрузки -- 18-31.07 alert=0 age 47..60 latest_month=6 -- 01-05.08 alert=1 age 61..65 latest_month=6 -- -- Механика: загрузка ходила раз в 28 дней и приносила период на месяц новее, а -- возраст считается от ПЕРВОГО ЧИСЛА покрытого месяца. Значит в момент самой -- свежей загрузки возраст уже ~46 (07-17 минус 06-01), к следующей дорастает до -- 46+28=74, и порог 60 лежит ВНУТРИ [46, 74] — тревога пересекала его каждый -- цикл, 14 суток из 28. Девять срабатываний, поданных в #2674 как улика застоя -- бенчмарка, — это замер НАШЕГО СОБСТВЕННОГО ТАКТА. После #2681 (WARNING → ERROR) -- это стало бы ежедневным событием две недели в месяц, гаснущим само собой — -- ровно та ложная тревога, которая приучает не читать алерты. -- -- ПОЧЕМУ ТАКТ, А НЕ ПОРОГ. Рассматривались два варианта: -- (A) поднять lag_allowance 25 → 40 (порог 75 против потолка 74). Запас ОДИН -- день: любой сдвиг окна/пропуск прогона на сутки — и ложная тревога -- возвращается. Порог при этом продолжает кодировать наш такт, а не -- поведение источника. Отклонено. -- (B) ЭТА миграция: такт 28 → 7. Потолок возраста становится floor+7 ≈ 53 при -- том же пороге 60 — запас 7 суток, т.е. один пропущенный недельный цикл -- поглощается, два подряд дают тревогу (и это уже осмысленная тревога). -- Порог 60 начинает означать ИМЕННО «Сбер перестал публиковать / загрузка -- сломалась», а не «мы давно не ходили». -- -- ЦЕНА. pull_sber_indices делает SBER_REF_AREAS (3: 643/66/77) × SBER_DASHBOARDS -- (3) = 9 GET-запросов к публичному неавторизованному sberindex.ru/api/sowa, без -- пауз в цикле; прод-прогон 2026-07-17 занял 4 секунды (counters.duration_sec=4, -- errors=0, upserted=639). Было 9 запросов / 28 дней, стало 9 / 7 дней = 36 в -- месяц. Это тот же эндпоинт, который дёргают сами дашборды Сбера при каждом -- открытии страницы; лимитов/бана на нём за всю историю прогонов не наблюдалось -- (0 ошибок в 6 прогонах). Риск нагрузки считаем отсутствующим. -- -- ПОБОЧНО. Оценщик имеет СВОЙ per-estimate guard свежести с порогом -- settings.sber_index_max_age_days=35. Он пробивается всегда, потому что возраст -- стартует с ~46. Недельный такт сокращает НАШУ задержку обнаружения с ≤28 суток -- до ≤7, то есть возраст = (лаг публикации Сбера) + ≤7 вместо + ≤28. Уйдёт ли он -- под 35 — зависит от того, когда Сбер реально публикует месяц (по нашим данным -- лаг публикации ≤46 и ≥31 суток, точнее по имеющимся прогонам не определить), -- поэтому НЕ обещаем починку этого guard'а, только снятие нашей части задержки. -- -- next_run_at подтягиваем на ближайшее окно (05:00-06:00 UTC): без этого правка -- default_params начнёт действовать только после уже запланированного прогона -- 2026-08-14, а до тех пор ложная тревога продолжала бы идти каждый день. -- LEAST() — чтобы повторное применение НИКОГДА не отодвигало прогон дальше. -- -- Идемпотентно: jsonb-конкатенация + LEAST, повторный прогон безопасен. -- Кода не меняет: interval_days читается kit-планировщиком из default_params -- (orchestration/scheduler.py::_defer_next_run_at, params.get("interval_days", 1)). BEGIN; UPDATE scrape_schedules SET default_params = COALESCE(default_params, '{}'::jsonb) || '{"interval_days": 7}'::jsonb, next_run_at = LEAST( next_run_at, ((CURRENT_DATE + INTERVAL '1 day') + make_interval(hours => 5)) AT TIME ZONE 'UTC' ) WHERE source = 'sber_index_pull'; COMMIT;