fix(tradein/scraper): сторож «ноль результатов» перестаёт читать «не измерено» как ноль (#2703) #2713
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2713
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2703-zero-result-watchdog-blind"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Сторож смотрел колонку
scrape_runs.total_seen, у которойDEFAULT 0. Замер по всем 53 источникам (прод, 2026-08-06):lots_fetched)unique_fetched— full-load'ы avito/cian/yandex)total_seenкак ключ counters не пишет ни один источник за всю историю — колонку заполняет_column_countsизlots_fetched.Полный список 28 слепых источников (по числу прогонов):
proxy_healthcheck1650,avito_detail_backfill76,deactivate_stale_avito62,rosreestr_dkp_import58,asking_to_sold_ratio_refresh56,refresh_search_matview55,newbuilding_enrich53,geocode_missing_listings53,yandex_detail_backfill52,deactivate_stale_yandex51,deactivate_stale_cian51,yandex_address_backfill50,cadastral_geo_match49,yandex_newbuilding_sweep41,house_imv_backfill39,deals_freshness_monitor36,geoportal_coords_backfill35,osm_poi_ekb_refresh35,domclick_detail_backfill30,deactivate_stale_domklik26,sber_freshness_monitor24,listing_source_snapshot22,cian_history_backfill18,sber_index_pull9,deal_city_price_bands_refresh6,house_dedup_merge6,rosreestr_quarter_poll6,deactivate_stale_n11.Для них сторож читал 0 всегда, в том числе у полностью успешного прогона → стрик не прерывался никогда → анти-спам «один раз на стрик» превращался в «один раз навсегда»: сработав однажды на третьем прогоне источника, сторож замолкал навсегда, продолжая выглядеть настроенным. Задача в issue называла три backfill'а — на деле задето 28 источников из 53.
Решение: не «свести счётчики» и не «выучить словари»
Свести счётчики к
total_seenневозможно и вредно: у 28 источников 28 разных словарей (deactivated/rows_written/poi_loaded/snapshotted/upserted/listings_matched…), уrefresh_search_matviewcounters пусты буквально ({}во всех 55 строках), у трёх мониторов результата нет по смыслу, а ноль уdeactivate_stale_*— здоровый ответ. Отображениеtotal_seen ↔ attemptedбыло бы прямо вредным: 1600 попыток с нулём обогащений читались бы как «результат 1600».Вместо этого:
counters, а не из колонки — «не измерено» (None) отличимо от «измерено, ноль»;_warn_source_has_no_result_metric, раз на источник за процесс) вместо молчаливого нуля;unique_fetched— 4 full-load'а входят в зону видимости (уcian_full_load6 из 38 успешных прогонов дали настоящий ноль) и перестают показыватьtotal_seen=0в админке.Зеро-прогоны backfill'ов при этом не остаются без присмотра: #2695 (
mark_backfill_finished) уже переводит их вfailed/banned, где их видит_alert_if_consecutive_failures.Test plan
tests/test_2703_zero_result_watchdog.py— 28 тестов на обеих копиях runs-модуля (kit + app): различие None/0, слепой источник не копит стрик и логирует, немеряный прогон прерывает стрик, настоящий нулевой стрик по-прежнему алертит (lots_fetchedиunique_fetched), анти-спам не сломан.(_run_result_count(...) or 0) == 0+ снятие guard'а) падают 4 ключевых теста на обеих копиях — ровно то поведение, что было наmain.tests/test_scrape_run_alert.py(#2625): строки теперь несутcounters, а не только колонку.test_search_cache_hit).tradein-mvp/packages/scraper-kit/**входит в scraper-allowlistdeploy-tradein.yml→ правка доедет доtradein-scraper. (app/services/scrape_runs.pyв allowlist НЕ входит — сам по себе он до скрапера не доехал бы.)Чего проверить нельзя
Поведенческая прод-верификация невозможна: получатель уведомлений в GlitchTip не настроен (#2673) — правил
0, получателей0, отправлено0за всю историю при 2861 накопленной группе ошибок. Событие создастся и никуда не уйдёт. Проверяется код в живом контейнере + тесты.Refs #2703
Сторож смотрел колонку scrape_runs.total_seen, у которой DEFAULT 0. Замер на проде (53 источника): колонку заполняет только lots_fetched — 21 источник, 455 прогонов. Ещё 4 источника (133 прогона, full-load'ы avito/cian/yandex) пишут unique_fetched, которого сторож не знал, хотя у cian_full_load 6 из 38 успешных прогонов дали настоящий ноль. Оставшиеся 28 источников (2650 прогонов) не пишут ни одного результатного ключа вовсе: proxy_healthcheck 1650, три detail- backfill'а, все deactivate_stale_*, refresh_search_matview с буквально пустыми {} во всех 55 строках. Для этих 28 сторож читал 0 ВСЕГДА, в том числе у полностью успешного прогона → стрик не прерывался никогда → анти-спам «один раз на стрик» превращался в «один раз навсегда»: сработав однажды на третьем прогоне источника, сторож замолкал навсегда, продолжая выглядеть настроенным. Свести счётчики к словарю сторожа невозможно: у 28 источников 28 разных словарей (deactivated / rows_written / poi_loaded / snapshotted / upserted / listings_matched …), у трёх мониторов результата нет по смыслу, а ноль у deactivate_stale_* — здоровый ответ. Поэтому: - признак берётся из counters, а не из колонки: «не измерено» (None) отличимо от «измерено, ноль»; - прогон без результатной метрики стрик ПРЕРЫВАЕТ — недостижимое условие прерывания стало невозможным по построению, а не по корректности счётчиков; - слепая зона логируется явно (раз на источник за процесс), а не молчит; - в словарь добавлен unique_fetched — 4 full-load'а входят в зону видимости сторожа и перестают показывать total_seen=0 в админке. Доставка уведомлений этим НЕ чинится: получатель в GlitchTip не настроен (#2673), за всю историю наружу не ушло ни одного события. Refs #2703