fix(tradein/proxy): доводить сигнал бана площадки до пула (#2600 п.1) #2653

Merged
bot-backend merged 1 commit from fix/tradein-ban-signal-to-pool into main 2026-08-05 11:37:36 +00:00

1 commit

Author SHA1 Message Date
d890e5cbe1 fix(tradein/proxy): доводить сигнал бана площадки до пула (#2600 п.1)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
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 2m44s
mark_health(ok) выставляется в __exit__ контекст-менеджера и ставит
ok=False только если из блока вылетело исключение. Мягкий бан приходит
валидным HTTP 200 с заглушкой (Авито «Доступ ограничен», капча Циана,
gate-заглушка Яндекса, QRATOR Домклика) и распознаётся ПОЗЖЕ, при разборе
HTML — уже вне блока. Итог: забаненный прокси получал ok=True,
consecutive_fails обнулялся, узел выдавали снова.

BrowserFetcher.report_ban() помечает текущий sticky-lease (#2640);
proxy_pool.mark_banned() выключает узел с disabled_reason='banned:<source>'
(колонка из #2610 — ipify-проба такой узел не воскрешает, иначе флаппинг).

Avito инструментирован на raise-сайтах, а не в __aexit__: pipeline
присваивает scraper._browser напрямую и не заходит в контекст-менеджер —
централизованный хук пропустил бы весь этот путь. Cian/Yandex проверяют
свои счётчики в __aexit__ ДО релиза lease. Curl-путь получил маркер
ProxyBanError (подмешан в Blocked-исключения; RateLimited намеренно нет —
он может быть нашим таймаутом).

Защита от выкоса пула: mark_banned не выключает узел, если он последний
достижимый для источника (EXISTS зеркалит логику acquire с учётом
affinity — domclick-узел не считается запасным для avito/cian/yandex).
pg_advisory_xact_lock сериализует проверку+апдейт: одностроч­ный UPDATE не
атомарен поперёк строк, два параллельных бана разных узлов могли пройти
мимо защиты и выкосить пул без самолечения.

Защита от ложного бана (deep-review): у Яндекса транспортные сбои
(_http_get глотал исключения и отдавал status_code=0) считались в тот же
счётчик, что настоящая капча — добавлен признак transport_error, счётчик
трогают только контентные провалы (Циан так делал изначально). Плюс порог
attempts>=3 на обоих триггерах: админский одиночный прогон делает ровно
одну попытку, и 1/1=100% выключал бы здоровый узел. Боевой sweep делает
>=4 попытки, детект настоящего бана не страдает.

Refs #2600
2026-08-05 16:33:26 +05:00