feat(tradein/avito): бан площадки не менял IP — прогон добивался в тот же забаненный адрес (#3283) #3313

Merged
lekss361 merged 1 commit from feat/3283g-rotate-on-platform-ban into main 2026-09-01 08:05:49 +00:00
Owner

Проблема

С #3304 сайдкар честно опознаёт бан-страницу Авито и поднимает AvitoBlockedError. Но реакция на неё осталась прежней: один bare-сброс контекста за прогон, дальше попытки уходят с того же exit-IP, который площадка уже отвергла. Смена адреса была только плановой, по счётчику попыток, и до конца прогона могла не наступить ни разу — прогон 5676 умер по block-ratio (14 блоков из 20), ни разу не сменив IP.

Ручная проверка на проде показала цену вопроса: rotate_proxy(db, 14)ok=True new_ip=178.176.79.156 reconnect=6.0, и бан-строка proxy_id=14/avito исчезла (ротация зовёт clear_source_bans). ~6 секунд против 12 часов простоя узла.

Почему это не воскрешает каскад #3251/#3212

Каскад был не «сброс контекста вреден сам по себе», а «сброс без смены IP, на том же забаненном адресе, бесполезен»: он выбрасывает уже пройденный QRATOR PoW, а площадка отвергает по адресу, и следующий блок приходит сразу же. Здесь сброс всегда идёт в связке с новым адресом, а новый адрес физически не может нести сожжённый PoW старого. Одноразовый bare-reset (без ротации) остался ровно один на прогон, как и был.

Что сделано

  • avito_detail_backfill_rotate_on_ban_max=2, ..._min_gap=10 — потолок ротаций по бану за прогон и минимальный разрыв в карточках. max=0 возвращает прежнее поведение байт-в-байт.
  • Бюджет тратится и на отказавшей ротации — иначе отказавший rotate_proxy дёргался бы на каждом следующем бане до конца прогона.
  • context_reset_used выставляется только по факту успеха: при тихом отказе rotate_proxy (исчерпан суточный лимит, нет rotate_url, сеть) сброса внутри не произошло, и съесть им одноразовый bare-reset значило бы остаться и без нового адреса, и без сброса вообще. Есть тест ровно на этот путь.
  • Логи _rotate_current_proxy получили ярлык триггера — rotate-on-ban против rotate-by-attempts. До этого обе ветки писали «rotate-by-attempts», и разбор прод-логов по факту бана вводил в заблуждение (замечание ревью).

Проверка

  • 5272 passed, 37 skipped — полный backend-прогон.
  • 5 новых тестов на ban-ветку, включая откат на bare-reset при упавшей ротации.
  • ruff чисто.

Не входит и остаётся открытым

  • Прокидывание behaviour-параметров сайдкара (#3308) через задачу/расписание и разнообразие якорей выдачи — отдельным PR.
  • clear_source_bans заодно обнуляет ban_count/эскалацию, так что действительно мёртвый узел не копит сигнал на вывод из пула.
  • _quota_used_today считает без блокировки — параллельные прогоны могут перебрать лимит; исчерпание квоты остаётся тихим ok=False.
## Проблема С #3304 сайдкар честно опознаёт бан-страницу Авито и поднимает `AvitoBlockedError`. Но реакция на неё осталась прежней: один bare-сброс контекста за прогон, дальше попытки уходят с **того же exit-IP**, который площадка уже отвергла. Смена адреса была только плановой, по счётчику попыток, и до конца прогона могла не наступить ни разу — прогон 5676 умер по block-ratio (14 блоков из 20), ни разу не сменив IP. Ручная проверка на проде показала цену вопроса: `rotate_proxy(db, 14)` → `ok=True new_ip=178.176.79.156 reconnect=6.0`, и бан-строка `proxy_id=14/avito` исчезла (ротация зовёт `clear_source_bans`). ~6 секунд против 12 часов простоя узла. ## Почему это не воскрешает каскад #3251/#3212 Каскад был не «сброс контекста вреден сам по себе», а «сброс **без смены IP**, на том же забаненном адресе, бесполезен»: он выбрасывает уже пройденный QRATOR PoW, а площадка отвергает по адресу, и следующий блок приходит сразу же. Здесь сброс всегда идёт **в связке с новым адресом**, а новый адрес физически не может нести сожжённый PoW старого. Одноразовый bare-reset (без ротации) остался ровно один на прогон, как и был. ## Что сделано - `avito_detail_backfill_rotate_on_ban_max=2`, `..._min_gap=10` — потолок ротаций по бану за прогон и минимальный разрыв в карточках. `max=0` возвращает прежнее поведение байт-в-байт. - Бюджет тратится **и на отказавшей** ротации — иначе отказавший `rotate_proxy` дёргался бы на каждом следующем бане до конца прогона. - `context_reset_used` выставляется только по факту успеха: при тихом отказе `rotate_proxy` (исчерпан суточный лимит, нет `rotate_url`, сеть) сброса внутри не произошло, и съесть им одноразовый bare-reset значило бы остаться и без нового адреса, и без сброса вообще. Есть тест ровно на этот путь. - Логи `_rotate_current_proxy` получили ярлык триггера — `rotate-on-ban` против `rotate-by-attempts`. До этого обе ветки писали «rotate-by-attempts», и разбор прод-логов по факту бана вводил в заблуждение (замечание ревью). ## Проверка - `5272 passed, 37 skipped` — полный backend-прогон. - 5 новых тестов на ban-ветку, включая откат на bare-reset при упавшей ротации. - `ruff` чисто. ## Не входит и остаётся открытым - Прокидывание `behaviour`-параметров сайдкара (#3308) через задачу/расписание и разнообразие якорей выдачи — отдельным PR. - `clear_source_bans` заодно обнуляет `ban_count`/эскалацию, так что действительно мёртвый узел не копит сигнал на вывод из пула. - `_quota_used_today` считает без блокировки — параллельные прогоны могут перебрать лимит; исчерпание квоты остаётся тихим `ok=False`.
lekss361 added 1 commit 2026-09-01 07:53:24 +00:00
feat(tradein/avito): бан площадки не менял IP — прогон добивался в тот же забаненный адрес (#3283)
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 8s
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 4m52s
2cef42dfa0
Сайдкар с #3304 честно опознаёт бан-страницу и поднимает AvitoBlockedError, но
реакция на неё оставалась прежней: один bare-сброс контекста за прогон и дальше
попытки с ТОГО ЖЕ exit-IP, который площадка уже отвергла. Ротация была только
плановой, по счётчику попыток, и до конца прогона могла не наступить ни разу.

Теперь бан — самостоятельный триггер смены адреса, но с потолком, потому что
безоглядный сброс контекста на каждый блок даёт самоподдерживающийся каскад
(#3251/#3212): он выбрасывает пройденный QRATOR PoW, а адрес остаётся тем же.
Здесь этого не происходит — новый IP физически не несёт сожжённый PoW старого.

- avito_detail_backfill_rotate_on_ban_max=2, ..._min_gap=10 (карточек между
  ротациями). max=0 возвращает прежнее поведение байт-в-байт.
- Бюджет тратится и на ОТКАЗАВШЕЙ ротации — иначе отказавший rotate_proxy
  дёргался бы на каждом следующем бане до конца прогона.
- context_reset_used выставляется только по факту успеха: при тихом отказе
  rotate_proxy (исчерпан лимит, нет rotate_url, сеть) сброса внутри не было, и
  съесть им одноразовый bare-reset значило бы остаться и без адреса, и без сброса.
- Логи _rotate_current_proxy получили ярлык триггера: rotate-on-ban против
  rotate-by-attempts, иначе разбор прод-логов по факту бана вводит в заблуждение.

Тесты: 5 на ban-ветку, включая падение ротации → откат на bare-reset.
lekss361 merged commit add974db65 into main 2026-09-01 08:05:49 +00:00
lekss361 deleted branch feat/3283g-rotate-on-platform-ban 2026-09-01 08:05: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#3313
No description provided.