fix(tradein/domclick): исчерпание пула прокси помечалось как отказ сбора (#3118) #3174

Merged
bot-backend merged 1 commit from fix/3118-domclick-no-proxy into main 2026-08-27 22:03:57 +00:00
Collaborator

Часть (б) из #3118: исчерпание пула прокси выглядело как отказ сбора площадкой.

Что было

NoProxyAvailableError поднимается из BrowserFetcher.__aenter___acquire_lease до первого HTTP-запроса. У соседей есть выделенная ветка (pipeline.py:3610, :3851, :4114), у run_domclick_city_sweep её не было — отказ проваливался в общий except Exception, _scraper_ref оставался пустым, и логика честного статуса ниже видела «0 лотов и есть ошибки» → mark_failed("fetch errors — 0 listings"), ban_kind = NULL.

Прод-след, прогон 5023 (27.08): умер за 51 мс, errors_count = 1, ban_kind = NULL. Со стороны неотличимо от «Домклик нас забанил», хотя площадку даже не запрашивали — в пуле из четырёх узлов не осталось свободных (аренда держится на весь прогон, а в тот момент жили четыре прогона одновременно).

Что стало

Отдельная ветка except NoProxyAvailableError перед общим except Exception (порядок обязателен — это подкласс Exception), внутри неё mark_banned(..., ban_kind=ban_kind_of_exception(exc))BAN_KIND_INFRA. Отказ инфраструктуры теперь называется инфраструктурой.

Ветку пришлось разместить во внутреннем блоке SERP-фазы: у домклика структура иная, чем у avito/cian/yandex — фаза обёрнута в asyncio.wait_for со своим try/except, а не идёт прямым async with Scraper() в основном try.

Заодно унаследованный чекпоинт (done_buckets) сохраняется при таком отказе — иначе наш собственный сбой стирал бы прогресс возобновления.

Проверка

Тест проверен на обратимость: без обработчика падает с assert ['failed'] == ['banned'] и логом mark_failed("fetch errors — 0 listings") — воспроизводит прод-баг один в один. С фиксом проходит, ban_kind == "infra". Смежные 88 domclick-тестов зелёные.

Что НЕ входит

Часть (а) — метрика свежести по status = 'done', из-за которой источник числился мёртвым, пока собирал данные — вынесена в #3172. Причина исчерпания пула (аренда на весь прогон + зомби-прогоны, держащие узлы часами) — отдельная тема, смежная с #2638 и #3171.

Refs #3118, #2657

Часть (б) из #3118: исчерпание пула прокси выглядело как отказ сбора площадкой. ## Что было `NoProxyAvailableError` поднимается из `BrowserFetcher.__aenter__` → `_acquire_lease` **до** первого HTTP-запроса. У соседей есть выделенная ветка (`pipeline.py:3610`, `:3851`, `:4114`), у `run_domclick_city_sweep` её не было — отказ проваливался в общий `except Exception`, `_scraper_ref` оставался пустым, и логика честного статуса ниже видела «0 лотов и есть ошибки» → `mark_failed("fetch errors — 0 listings")`, `ban_kind = NULL`. Прод-след, прогон **5023** (27.08): умер за **51 мс**, `errors_count = 1`, `ban_kind = NULL`. Со стороны неотличимо от «Домклик нас забанил», хотя площадку даже не запрашивали — в пуле из четырёх узлов не осталось свободных (аренда держится на весь прогон, а в тот момент жили четыре прогона одновременно). ## Что стало Отдельная ветка `except NoProxyAvailableError` перед общим `except Exception` (порядок обязателен — это подкласс `Exception`), внутри неё `mark_banned(..., ban_kind=ban_kind_of_exception(exc))` → `BAN_KIND_INFRA`. Отказ инфраструктуры теперь называется инфраструктурой. Ветку пришлось разместить во внутреннем блоке SERP-фазы: у домклика структура иная, чем у avito/cian/yandex — фаза обёрнута в `asyncio.wait_for` со своим `try/except`, а не идёт прямым `async with Scraper()` в основном `try`. Заодно унаследованный чекпоинт (`done_buckets`) сохраняется при таком отказе — иначе наш собственный сбой стирал бы прогресс возобновления. ## Проверка Тест проверен на обратимость: без обработчика падает с `assert ['failed'] == ['banned']` и логом `mark_failed("fetch errors — 0 listings")` — воспроизводит прод-баг один в один. С фиксом проходит, `ban_kind == "infra"`. Смежные 88 domclick-тестов зелёные. ## Что НЕ входит Часть (а) — метрика свежести по `status = 'done'`, из-за которой источник числился мёртвым, пока собирал данные — вынесена в #3172. Причина исчерпания пула (аренда на весь прогон + зомби-прогоны, держащие узлы часами) — отдельная тема, смежная с #2638 и #3171. Refs #3118, #2657
bot-backend added 1 commit 2026-08-27 21:56:47 +00:00
fix(tradein/domclick): исчерпание пула прокси свипа теперь infra-бан, не отказ сбора
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
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 / 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 4m42s
ade1a065d6
NoProxyAvailableError поднимается из BrowserFetcher.__aenter__ (_acquire_lease)
ДО первого HTTP-запроса, когда пул прокси пуст — это НАША инфраструктура, не
блокировка площадкой. У run_avito_full_load/run_cian_full_load/run_yandex_full_load
уже есть выделенный except NoProxyAvailableError -> mark_banned(ban_kind='infra'),
у run_domclick_city_sweep его не было: исключение проваливалось в общий except
Exception внутри SERP-фазы, _scraper_ref оставался пустым, и честный статус ниже
видел "0 лотов + errors>0" -> mark_failed("fetch errors — 0 listings") с
ban_kind=NULL. Прод-факт: run 5023 (27.08) умер за 51 мс, errors_count=1,
ban_kind=NULL — неотличимо от честного отказа сбора площадкой.

Добавлен except NoProxyAvailableError перед generic except Exception (порядок
важен: класс — подкласс RuntimeError). Обработчик зеркалит avito/cian/yandex:
mark_banned + ban_kind_of_exception(exc) (даёт BAN_KIND_INFRA), и сохраняет
унаследованный чекпоинт (skip_buckets) вместо потери его на нашем же отказе.

Тест test_3118_domclick_no_proxy.py проверен на обратимость: без обработчика
падает (mark_failed вместо mark_banned), с обработчиком — проходит.
bot-backend merged commit bdb9b64b03 into main 2026-08-27 22:03:57 +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#3174
No description provided.