fix(scraper-kit): бан площадки не гасит здоровье узла для всех источников — report_ban по паре узел×источник вместо mark_health(ok=False) #3357

Merged
bot-backend merged 2 commits from fix/3288-avito-ban-per-source into main 2026-09-05 18:36:30 +00:00
Collaborator

Refs #3288, часть A (kit-side). Части B (breaker record_block(kind) + NoProxy-stop в avito_detail_backfill) и C (сайдкар шлёт бан-страницу кодом 500) — отдельно.

Что уже было в main

Пункты 1-2 постановки (ветка except SidecarBanPageError до общего except в providers/avito/detail.py:581 + report_ban там же) закрыты #3304 — не дублировано.

Что чинит этот PR (главное следствие из комментариев #3288)

browser_fetcher.py: SidecarBanPageError наследует httpx.HTTPStatusError и попадал в общий except Exception_report_fetch_result(False)mark_health(ok=False)глобальный карантин узла: три бана Авито подряд выносили узел и для Домклика, и для Циана (наблюдалось 31.08 и 01.09: все три узла на consecutive_fails=3, banned_for_source=0, живые по пробе). Теперь отдельная ветка в _post_fetch/_post_fetch_json_report_platform_ban() = report_ban() (бан по паре узел×источник, scrape_proxy_source_bans) + _report_fetch_result(False, health=False) — kwarg выключает ровно mark_health; touch-heartbeat и fail-streak-ротация остаются.

Дедуп report_ban по lease-id: одно событие бана доезжало трижды (_post_fetch, ретрай fetch(), провайдер сверху), каждый mark_banned растит ban_count (6ч→12ч) — без дедупа фикс кратно удлинял бы отдых пары.

source фетчер знает сам (self._source, один фетчер на источник) — проводки не потребовалось. NoProxyAvailableError поднимается в _acquire_lease ДО POST'а — health/report_ban не звучат; тип остаётся в __cause__ для части B (2 теста).

Тесты

test_3288_avito_ban_per_source.py (7): 790 passed, 2 skipped (-k avito/ban/proxy/fetcher). Фальсификация: F1 (откат diff в fetcher) → бан площадки не должен трогать здоровье узла: [(13, False), (13, False)]; F2 (мутация смерженной защиты detail.py) → AvitoSidecarUnavailableError (infra вместо platform).

Прод-приёмка

После следующего avito-прогона с банами: узлы в scrape_proxy_source_bans для avito, consecutive_fails у них НЕ растёт; cian/domclick продолжают получать узлы (ProxyPoolExhaustedError … unhealthy_or_disabled=7 больше не появляется).

Оставлено сознательно: fetch() ретраит бан-страницу вторым POST'ом — менять retry-политику без замера транзитного бюджета serp.py не стали.

Refs #3288, часть A (kit-side). Части B (breaker `record_block(kind)` + NoProxy-stop в avito_detail_backfill) и C (сайдкар шлёт бан-страницу кодом 500) — отдельно. ## Что уже было в main Пункты 1-2 постановки (ветка `except SidecarBanPageError` до общего `except` в `providers/avito/detail.py:581` + `report_ban` там же) закрыты #3304 — не дублировано. ## Что чинит этот PR (главное следствие из комментариев #3288) `browser_fetcher.py`: `SidecarBanPageError` наследует `httpx.HTTPStatusError` и попадал в общий `except Exception` → `_report_fetch_result(False)` → `mark_health(ok=False)` — **глобальный** карантин узла: три бана Авито подряд выносили узел и для Домклика, и для Циана (наблюдалось 31.08 и 01.09: все три узла на `consecutive_fails=3`, `banned_for_source=0`, живые по пробе). Теперь отдельная ветка в `_post_fetch`/`_post_fetch_json` → `_report_platform_ban()` = `report_ban()` (бан по паре узел×источник, `scrape_proxy_source_bans`) + `_report_fetch_result(False, health=False)` — kwarg выключает ровно `mark_health`; touch-heartbeat и fail-streak-ротация остаются. Дедуп `report_ban` по lease-id: одно событие бана доезжало трижды (`_post_fetch`, ретрай `fetch()`, провайдер сверху), каждый `mark_banned` растит `ban_count` (6ч→12ч) — без дедупа фикс кратно удлинял бы отдых пары. `source` фетчер знает сам (`self._source`, один фетчер на источник) — проводки не потребовалось. `NoProxyAvailableError` поднимается в `_acquire_lease` ДО POST'а — health/report_ban не звучат; тип остаётся в `__cause__` для части B (2 теста). ## Тесты `test_3288_avito_ban_per_source.py` (7): `790 passed, 2 skipped` (-k avito/ban/proxy/fetcher). Фальсификация: F1 (откат diff в fetcher) → `бан площадки не должен трогать здоровье узла: [(13, False), (13, False)]`; F2 (мутация смерженной защиты detail.py) → `AvitoSidecarUnavailableError` (infra вместо platform). ## Прод-приёмка После следующего avito-прогона с банами: узлы в `scrape_proxy_source_bans` для avito, `consecutive_fails` у них НЕ растёт; cian/domclick продолжают получать узлы (`ProxyPoolExhaustedError … unhealthy_or_disabled=7` больше не появляется). Оставлено сознательно: `fetch()` ретраит бан-страницу вторым POST'ом — менять retry-политику без замера транзитного бюджета serp.py не стали.
bot-backend added 1 commit 2026-09-05 18:06:21 +00:00
fix(tradein/proxy): бан площадки снимает узел адресно, а не гасит его здоровье для всех источников
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 16s
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 6m10s
3b8545f609
Сайдкар на бан-странице Авито отвечает HTTP 500 с ban_page-маркером, клиент
поднимает SidecarBanPageError — но она подкласс httpx.HTTPStatusError, и общий
except Exception в _post_fetch звал mark_health(ok=False). Это ГЛОБАЛЬНОЕ решение
по узлу: три бан-страницы Авито выбивали его из выдачи и Яндексу, и Циану, и
Домклику (прод-замер 31.08-01.09: узлы 9/13/14 на потолке MAX_CONSECUTIVE_FAILS
при banned_for_source=0, живая проба тех же узлов проходила).

Теперь бан-страница ловится отдельной веткой ДО общего except и уходит в
mark_banned(source=...) — приговор паре «узел×источник», которую фильтрует
acquire(source). Здоровье узла не трогаем; транспортный сбой (таймаут, плоская
500) как и раньше идёт в mark_health(ok=False). report_ban дедуплицирован по
lease: одно событие доезжало до него трижды (POST, ретрай fetch(), провайдер), а
каждый вызов растит ban_count и кратно удлиняет отдых пары.

Refs #3288
Light1YT added 1 commit 2026-09-05 18:25:34 +00:00
fix(tradein/proxy): бан площадки рапортует только фетчер — ротация больше не банит свежий узел
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
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 5m1s
c57138f4c7
Дедуп report_ban по _banned_lease_id не достигал цели при ротации. Узел 13 ловит
бан-страницу → фетчер репортит бан 13 и по fail-streak меняет lease на 14 →
провайдерский report_ban в providers/avito/detail.py видит уже сброшенный
_banned_lease_id и банит СВЕЖИЙ узел 14, который к площадке не ходил. При трёх узлах
в пуле одна бан-страница выбивала две трети выдачи на 6 часов с эскалацией ban_count.

Убран провайдерский report_ban на ветках SidecarBanPageError в avito/detail.py и
domclick/detail.py: фетчер репортит сам, раньше и по правильному lease. Детекты не от
сайдкара (firewall / 0 карточек в serp.py, QRATOR-маркеры parse_detail_html) фетчеру
не видны — там report_ban остаётся.

Плюс два смежных: fetch()-ретрай ловил httpx.HTTPError, подклассом которого является
SidecarBanPageError, — каждая бан-страница стоила 2 POST'а и +2 к fail-streak (ротация
вдвое раньше задуманного); и NoProxyAvailableError из ротационного _acquire_lease внутри
_report_platform_ban вылетала ВМЕСТО SidecarBanPageError, подменяя диагноз platform на
infra — теперь ротация там best-effort.

Тесты: рабочий пул теперь РОТИРУЮЩИЙ (13→14) — на неподвижном пуле дефект физически не
проявляется. Два теста, пинившие прежний контракт (провайдер репортит), инвертированы:
у них MagicMock-фетчер, который настоящего рапорта не делает.

Refs #3288
bot-backend merged commit 63dbc209b2 into main 2026-09-05 18:36:30 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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#3357
No description provided.