fix(tradein/scraper): длинная серия неудач перестаёт замолкать, оборванный прогон — называться успехом (#2670) #2720

Merged
bot-backend merged 2 commits from fix/2670-streak-and-coverage into main 2026-08-06 10:06:58 +00:00

2 commits

Author SHA1 Message Date
a5ce832069 merge main (#2702 clock_timestamp) в ветку #2670
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m5s
2026-08-06 14:56:41 +05:00
fb4e44e521 fix(tradein/scraper): длинная серия неудач перестаёт замолкать, оборванный прогон — называться успехом (#2670)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m1s
1. Анти-спам «один раз на стрик» был заперт в «один раз навсегда». Оба сторожа
слали алерт РОВНО на N-й подряд неудаче и дальше молчали: пока источники падали
вперемешку с успехами, серия рвалась и алерт взводился заново, а у постоянно
сломанного источника рваться нечему. Прод 2026-08-06: avito_full_load — 31 неудача
подряд, последний успешный прогон 03.07 (34 дня без сбора), алерт был ровно один;
avito_full_load_exhaustive — 5 подряд.

Условие прерывания у этого сторожа, в отличие от #2703, ДОСТИЖИМО — по данным прода
оно достигается (domclick_city_sweep: текущий стрик 1 при 47 завершённых прогонах).
Ломается не достижимость, а срок: реальный простой длиннее одного напоминания.
Поэтому вместо «ровно N» — разреженная лестница вех N, 2N, 4N…, дальше не реже
одной на STREAK_ALERT_MAX_PERIOD×N прогонов; достигнутый потолок сканирования сам
по себе повод для алерта, так что «замолчать навсегда» невозможно по построению.
Лестница по ПРОГОНАМ, а не календарная: источники идут разным тактом (domclick —
раз в сутки, proxy_healthcheck — раз в полчаса).

2. Признак обрыва рождался в цикле fetch_city по ROOM_BUCKETS (break на блоке,
continue на битом бакете) и наружу не выходил: метод возвращает голый список лотов,
а pipeline писал в counters.pages_fetched расчётную оценку «бакеты × страницы» —
оценку, не измерение. Прогон, прошедший 3 бакета из 6, был неотличим от полного и
уходил в done. Теперь охват считает сам скрейпер (buckets_completed/buckets_total)
и несёт наружу тем же каналом, что и blocked, — включая случай снятия фазы по
таймауту, где ссылка на скрейпер жива. Честный статус сравнивает собранное с
ожидаемым охватом, а не с нулём.

Порядок веток: блок (banned) → ноль лотов при ошибках (failed) → неполный охват
(failed) → done. Охват 0/0 (скрейпер не успел создаться) вердикта не выдумывает.

Заодно _DOMCLICK_NUM_BUCKETS перестаёт быть вторым литералом рядом с ROOM_BUCKETS.

На проде случаев неполного охвата пока не зафиксировано (0 прогонов domclick со
статусом done и errors_count>0) — это профилактика, и она честно измерима только
теперь: до правки охват нигде не сохранялся.

Refs #2670
2026-08-06 14:49:41 +05:00