gendesign/tradein-mvp/backend/data/sql/219_deactivate_stale_health_gate.sql
bot-backend 627e163103
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m56s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m16s
fix(tradein): TTL-деактивация не исполняется, пока сбор по источнику лежит (#2659) (#2710)
2026-08-06 08:40:41 +00:00

67 lines
4.9 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 219_deactivate_stale_health_gate.sql
-- Пороги гейта здоровья сбора для TTL-деактивации (#2659).
--
-- ЗАЧЕМ. TTL отвечает на вопрос «объявление сняли?», а меряет «мы его давно не
-- видели». Пока обход здоров, разница мала; когда обход лёг — разница равна всему
-- инвентарю. Прод, авито 10.07-26.07.2026: 17 суток подряд без единой собранной
-- страницы, TTL=10 снял 9 033 строки, из них 1 270 доказанно вернулись живыми,
-- как только сбор восстановился (сверка listing_source_snapshots с текущим
-- last_seen_at). Код гейта — app/tasks/deactivate_stale_avito.py.
--
-- ПОЧЕМУ НЕ ПО СТАТУСУ ПРОГОНА. Ban-детектор эти провалы НЕ ловит:
-- yandex 18.07-30.07 — 5 прогонов в сутки, ВСЕ 'done', НОЛЬ 'banned',
-- total_seen = 0 тринадцать суток подряд;
-- domklik 20.07-30.07 — 11 суток 'done' с total_seen = 0, а 02.08 TTL=14
-- снял 6 131 строку разом.
-- Поэтому здоровье меряется результатом (сколько строк источник реально подтвердил
-- свежими за 3 суток), а не статусом прогона.
--
-- ОТКУДА ЧИСЛА. Ряд «подтверждений за 3 суток» по дням восстановлен из
-- listing_source_snapshots (снимок last_seen_at на каждую дату), срез совпадает
-- со срезом соответствующего UPDATE (source + segments + та же колонка свежести):
-- avito (все сегменты, last_seen_at): здоровые сутки 3542..6079,
-- провал 08.07-29.07 — 0..970 -> 1500
-- yandex (vtorichka, last_seen_at): здоровые 897..2206, провал 0 -> 500
-- cian (vtorichka, last_seen_at): 748..4329, провалов не было -> 500
-- domklik (все сегменты, scraped_at): сейчас 62 за 3 суток, сбор
-- фактически стоит -> 200
-- Каждый порог лежит между максимумом провала и минимумом здоровых суток:
-- авито 970 < 1500 < 3542 — исторический случай ловится с запасом в обе стороны.
--
-- domklik ЗАБЛОКИРУЕТСЯ СРАЗУ, и это верный исход, а не сбой миграции: источник
-- подтверждает ~50 строк в сутки, TTL по нему уже один раз (02.08) снёс инвентарь
-- целиком. Пока сбор не восстановлен, деактивации там нечего подтверждать; протухшие
-- строки закрываются фильтром свежести на стороне чтения (#2656), а не TTL.
--
-- Цена ошибки асимметрична: пропущенная деактивация чинится следующим прогоном,
-- ложная — только повторным сбором, которого может не быть. Пороги поэтому
-- смещены в сторону «пропустить прогон».
--
-- ЗАВИСИМОСТИ: 052_scrape_schedules.sql (таблица + UNIQUE(source)),
-- 090/115/160 (сами расписания deactivate_stale_*).
-- ТОЛЬКО данные (UPDATE default_params), DDL нет.
-- Идемпотентность + уважение к ручной настройке: ключ проставляется лишь там, где
-- его ещё нет, поэтому повторный прогон файла не затирает подкрученное оператором
-- значение. Снять гейт вручную: min_confirmations = 0.
BEGIN;
UPDATE scrape_schedules
SET default_params = default_params || jsonb_build_object('min_confirmations', 1500),
updated_at = NOW()
WHERE source = 'deactivate_stale_avito'
AND NOT default_params ? 'min_confirmations';
UPDATE scrape_schedules
SET default_params = default_params || jsonb_build_object('min_confirmations', 500),
updated_at = NOW()
WHERE source IN ('deactivate_stale_cian', 'deactivate_stale_yandex')
AND NOT default_params ? 'min_confirmations';
UPDATE scrape_schedules
SET default_params = default_params || jsonb_build_object('min_confirmations', 200),
updated_at = NOW()
WHERE source = 'deactivate_stale_domklik'
AND NOT default_params ? 'min_confirmations';
COMMIT;