fix(tradein/scrapers): прогон больше не рапортует «done» поверх провала и нуля #2892
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#2892
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-honest-run-status"
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?
Проблема
Из аудита 15.08. Это усилитель всех остальных проблем со сбором: пока прогоны рапортуют успех, деградация источников не видна ни на одном экране, и мы узнаём о ней только когда клиент не получает оценку.
Три части, все проверены по прод-БД:
Доля отказов не влияла на статус.
avito_detail_backfill15.08:{"attempted":64,"failed":57,"enriched":6}→status='done'. 89% отказов, галка зелёная.Сторож нулевого результата был слеп к части прогонов.
yandex_newbuilding_sweep— десять прогонов подряд с 26.07 по 10.08, всеdoneприsucceeded=0, rows_inserted=0. Причина:_RESULT_COUNTER_KEYSперечислял ключи, которых этот sweep не пишет.Витрина читала не те ключи.
new_countпоказывал ноль при реальныхsaved_inserted482 / 214 / 239.Что сделано
Правило по доле отказов, дополненные результатные ключи и починенное чтение витрины.
Что поймало ревью
Первый круг — MAJOR, и замечание оказалось точным: в результатные ключи попал голый
rows_inserted, который на проде пишет не только целевой sweep, но иrosreestr_dkp_import— 67 прогонов за 90 суток, из них 66 штатно нулевые (данные приходят раз в квартал). Правка объявила бы ежедневный импорт сделок сломанным.Заменено на
succeeded. Проверено запросом: этот ключ пишут ровно два источника —yandex_newbuilding_sweepиnewbuilding_enrich, аrosreestr_dkp_importне пишет ни разу.Заодно убран
processed: это счётчик попыток, а не результата — уnewbuilding_enrichон равен лимиту даже при полном провале.Одно замечание автор опроверг, и обоснованно: ревьюер сам пометил его LOW и предложил закрыть не кодом, а фиксацией в описании. Фиксирую ниже.
Второй круг: MINOR.
Ожидаемый побочный эффект — следить первые ночи
Backfill'ы с высокой долей отказов теперь получают
failedвместоdone. Суточная сводка планировщика считает просроченным источник по возрасту последнего успешного прогона — значит такие источники начнут попадать в «просрочено».Это не регресс, а именно то, чего мы добивались: раньше они выглядели живыми, будучи мёртвыми. Но первые пару ночей сводка будет непривычно красной, и важно не принять это за поломку. Сейчас под правило попадает прежде всего
avito_detail_backfill(HTTP 439, #2827) — то есть сводка начнёт показывать ровно ту проблему, которая реально есть.Test plan
Три прод-факта, где status='done' врал о реальном исходе прогона: - avito_detail_backfill 15.08: {"attempted":64,"failed":57,"enriched":6,"blocked":1} -> 'done'. mark_backfill_finished звал mark_done, потому что produced=6 (>0); ни _sweep_run_did_nothing (нет anchors_total/errors_count у backfill'ов), ни _phase_totally_failed (голые "attempted"/"failed" без фазового префикса) эту форму counters не ловили. Новый _failed_ratio_too_high внутри mark_done: failed/attempted >= 0.5 -> 'failed', >= 0.15 -> тоже 'failed' (другая формулировка причины в error-тексте) — 'partial' статусом не заведён: это потребовало бы DROP+ADD CHECK constraint (051_scrape_runs_extend.sql) и дообучения ещё 4 мест (Literal-фильтр admin API, статусы фронта, оба IN-списка сторожей) — тот же класс проводки, что и у ban_kind (#2686/#2764), который сознательно не стал новым статусом. - yandex_newbuilding_sweep 26.07-10.08: десять прогонов подряд 'done' при processed=5 succeeded=0 rows_inserted=0 failed_resolve=4-5 — сторож нулевого результата (_alert_if_consecutive_zero_results) не видел ни один результатный ключ этого sweep'а и молчал навсегда. _RESULT_COUNTER_KEYS дополнен rows_inserted/processed (именно в этом порядке — rows_inserted это результат, processed это попытки; иначе "5 обработано, 0 записано" замаскировалось бы под measured-5). - admin-витрина показывала new_count=0 у трёх подряд cian_full_load при реально сохранённых saved_inserted=482/214/239 — full-load'ы не пишут ни 'new_count', ни 'lots_inserted'. _column_counts дополнен saved_inserted/rows_inserted. Правки продублированы в scraper_kit/orchestration/runs.py (byte-эквивалент app.services.scrape_runs, см. докстринг модуля) для параллели: единственный текущий писатель "attempted"/"failed" (mark_backfill_finished) живёт только в app-копии, но приоритет ключей/константы держим синхронными на будущее. Не тронуто: сознательно пустые sweep'ы (errors_count=0, honest empty) и малые батчи (attempted < 3) — доля отказов на них не считается диагнозом. Tests: tests/test_honest_run_status_failed_ratio.py (41 кейс, оба модуля, включая точные прод-числа из трёх фактов выше) + regression-прогон 609 тестов по всем файлам, трогающим scrape_runs/orchestration.runs — 0 регрессий.