fix(tradein/avito): серия отказов обрывается и называет причину, а не выедает бюджет (#2674) #2739

Merged
bot-backend merged 1 commit from fix/avito-backfill-failure-breaker into main 2026-08-06 15:29:49 +00:00
Collaborator

Что было

avito_detail_backfill, три ночи подряд (3, 4, 5 августа): attempted≈1600, failed≈1600, blocked=0, enriched=0, полный бюджет 9000 с и ~1600 запросов через единственный прокси. Брейкер у задачи был только на блоки; отказ, не распознанный как блок, не обрывал ничего.

Обогащения у этой задачи нет с 12 июля (последний прогон с enriched>0 — 2026-07-12 01:08). Остальные ночи выглядели так: либо attempted=5, blocked=5 (13-22 июля, 1, 2 и 6 августа), либо attempted=0 (23-30 июля, очередь была пуста), либо ~1600 отказов.

Почему причина неизвестна — и почему это чинится кодом

Поштучные отказы логируются WARNING; в GlitchTip уезжают только ERROR, а логи контейнера пропадают при его пересоздании — сегодня контейнер пересоздан в 12:57, и прогоны 11:05 и 12:40 остались без логов вовсе. Различие «площадка нас заблокировала» vs «не поднялся наш прокси-тракт» — это разные владельцы задачи (#2686, #2698), и оно обязано переживать перезапуск контейнера, то есть лежать в самом прогоне.

Обе версии сегодня равновероятны и не разводятся имеющимися данными:

  • 0,46 с чистого времени на карточку сверх паузы — быстрый отказ; ровно так выглядит curl: (56) CONNECT tunnel failed, response 502, который в тот же день ловил соседний cian_city_sweep (run 3309, 3 подряд, abort);
  • в те же дни у Авито шли блоки по браузерному тракту (Avito detail firewall/soft-block) — то есть площадка тоже нас видит.

Что сделано

  • max_consecutive_failures (по умолчанию 25) обрывает серию отказов без единого успеха. Порог намеренно выше блочного: пачка мёртвых карточек (404 → ValueError в curl-режиме) не должна ронять здоровый прогон, а 25 подряд — уже не невезение;
  • перепись причин: тип исключения + текст без URL (иначе 1600 отказов дают 1600 «разных» причин), самая частая с долей уходит в текст статуса через mark_backfill_finished(fail_hint=…). Прогон 3306 сказал бы не «blocked=5», а какой именно блок и чей;
  • счётчик серии обнуляют успех и честный 404 площадки (тракт цел); блочного брейкера 404 по-прежнему не касается (#2034).

Обхода антибота здесь нет и не предполагается: цель — чтобы завтрашний прогон сам назвал владельца отказа.

Test plan

  • pytest tests/tasks/test_avito_detail_backfill.py — 20 passed; тест на брейкер падает на старом коде (attempted=200 вместо 25)
  • полный pytest бэкенда — 3854 passed
  • прод: окно 12:39 UTC — в scrape_runs.error должна появиться строка «причина: <класс>: <текст> (N из M)»

Refs #2674

## Что было `avito_detail_backfill`, три ночи подряд (3, 4, 5 августа): `attempted≈1600, failed≈1600, blocked=0, enriched=0`, полный бюджет 9000 с и ~1600 запросов через единственный прокси. Брейкер у задачи был **только на блоки**; отказ, не распознанный как блок, не обрывал ничего. Обогащения у этой задачи нет с **12 июля** (последний прогон с `enriched>0` — 2026-07-12 01:08). Остальные ночи выглядели так: либо `attempted=5, blocked=5` (13-22 июля, 1, 2 и 6 августа), либо `attempted=0` (23-30 июля, очередь была пуста), либо ~1600 отказов. ## Почему причина неизвестна — и почему это чинится кодом Поштучные отказы логируются `WARNING`; в GlitchTip уезжают только `ERROR`, а логи контейнера пропадают при его пересоздании — сегодня контейнер пересоздан в 12:57, и прогоны 11:05 и 12:40 остались без логов вовсе. Различие «площадка нас заблокировала» vs «не поднялся наш прокси-тракт» — это разные владельцы задачи (#2686, #2698), и оно обязано переживать перезапуск контейнера, то есть лежать в самом прогоне. Обе версии сегодня равновероятны и не разводятся имеющимися данными: - 0,46 с чистого времени на карточку сверх паузы — быстрый отказ; ровно так выглядит `curl: (56) CONNECT tunnel failed, response 502`, который в тот же день ловил соседний `cian_city_sweep` (run 3309, 3 подряд, abort); - в те же дни у Авито шли блоки по браузерному тракту (`Avito detail firewall/soft-block`) — то есть площадка тоже нас видит. ## Что сделано - `max_consecutive_failures` (по умолчанию 25) обрывает серию отказов без единого успеха. Порог намеренно выше блочного: пачка мёртвых карточек (404 → `ValueError` в curl-режиме) не должна ронять здоровый прогон, а 25 подряд — уже не невезение; - перепись причин: тип исключения + текст **без URL** (иначе 1600 отказов дают 1600 «разных» причин), самая частая с долей уходит в текст статуса через `mark_backfill_finished(fail_hint=…)`. Прогон 3306 сказал бы не «blocked=5», а какой именно блок и чей; - счётчик серии обнуляют успех и честный 404 площадки (тракт цел); блочного брейкера 404 по-прежнему не касается (#2034). Обхода антибота здесь нет и не предполагается: цель — чтобы завтрашний прогон сам назвал владельца отказа. ## Test plan - [x] `pytest tests/tasks/test_avito_detail_backfill.py` — 20 passed; тест на брейкер падает на старом коде (`attempted=200` вместо 25) - [x] полный `pytest` бэкенда — 3854 passed - [ ] прод: окно 12:39 UTC — в `scrape_runs.error` должна появиться строка «причина: <класс>: <текст> (N из M)» Refs #2674
bot-backend added 1 commit 2026-08-06 15:17:50 +00:00
fix(tradein/avito): серия отказов обрывается и называет причину, а не выедает бюджет (#2674)
All checks were successful
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m10s
75f2f62a38
Прогоны avito_detail_backfill 3-5 августа: attempted≈1600, failed≈1600,
blocked=0, enriched=0, полные 9000 с бюджета и 1600 запросов через единственный
прокси — каждую ночь. Брейкер у задачи был только на блоки; отказы, не
распознанные как блок, не обрывали ничего.

Хуже, что причина этих 1600 отказов невосстановима: поштучно они логируются
WARNING, в GlitchTip уезжают только ERROR, а логи контейнера пропадают на первом
же деплое (проверено: контейнер пересоздан в 12:57, прогоны 11:05 и 12:40 уже
без логов). Разница между «площадка блокирует» и «не поднялся наш прокси-тракт»
— разные владельцы задачи (#2686, #2698), и она обязана переживать перезапуск
контейнера.

Поэтому:
- max_consecutive_failures (по умолчанию 25) обрывает серию отказов без единого
  успеха. Порог выше блочного намеренно: пачка мёртвых карточек (404 → ValueError
  в curl-режиме) не должна ронять здоровый прогон;
- задача ведёт перепись причин (тип исключения + текст без URL) и отдаёт самую
  частую с её долей в текст статуса прогона — mark_backfill_finished(fail_hint=).
  Прогон 3306 сегодня сказал бы не «blocked=5», а какой именно блок и чей.

Счётчик серии обнуляют и успех, и честный 404 площадки: он доказывает, что тракт
цел (блочного брейкера 404 по-прежнему не касается, #2034).
bot-backend merged commit 0dc6f12630 into main 2026-08-06 15:29:49 +00:00
bot-backend deleted branch fix/avito-backfill-failure-breaker 2026-08-06 15:29:49 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2739
No description provided.