fix(avito): graceful sidecar HTTP 500/503 in browser SERP fetch (#1790) #1896
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#1896
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/avito-full-load-browser-500"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Контекст
Follow-up к #1790. Часть 1 issue (
AVITO_PROXY_MAX_ROTATIONS=0→4) уже исправлена на проде — бан-шторм прекратился (с 06-18 нольbannedуavito_full_load, см. comment). Этот PR чинит новый доминирующий отказ: browser-sidecar HTTP 500/503.Проблема
SERP-фетч идёт через
tradein-browsersidecar (POST /fetch) сBrowserFetcher. Sidecar отдаёт HTTP 500 на любую внутреннюю ошибку страницы (тело{"error": "ErrorType: message"}) —NS_ERROR_PROXY_TOO_MANY_REQUESTS(soft proxy-бан),Page.goto Timeout(transient) — и HTTP 503 когда браузер не поднялся._fetch_serp_html_browserвызывалself._browser.fetch(url)без обёртки — обрабатывал только rendered firewall-HTML.httpx.HTTPStatusErrorот sidecar всплывал неперехваченным → genericexcept Exceptionвrun_avito_full_load→mark_failed+ re-raise.Прод-инцидент run #356 (06-24):
unique_fetched=3207, saved_inserted=466, saved_updated=2720, затем один поздний 500 → весь run помеченfailed, хотя 3186 карточек уже сохранены через инкрементальныйon_bucket. Один транзиентный 500 и нукал статус, и обрывал остаток bucket-walk.Фикс
_fetch_serp_html_browser: ловимhttpx.HTTPStatusError, устойчиво парсим тело, классифицируем и роутим в существующую rotation/retry-машинерию:too_many_requests/ status 503 /browser unavailablemax_rot)Timeout/ прочие 5xx_AVITO_SIDECAR_TRANSIENT_RETRIES=2)raise AvitoRateLimitedError→ gracefulmark_banned(partial preserved)AvitoRateLimitedErrorловится per-bucket tolerance вfetch_all_secondary(одиночный блок → бакет скипается, обход продолжается; только N-подряд абортит) и run-levelexcept (AvitoBlockedError, AvitoRateLimitedError)→mark_banned. Termination гарантирован: каждыйcontinueтратит конечный бюджет (rot_done<max_rotлибоtransient_left>0); worst-case =max_rot + 2 + 1итераций.Намеренно НЕ blindly
continue— это добило бы rate-limited прокси.Изменение маршрутизации (для пост-review)
Раньше sidecar-500 на probe-странице ловился в
_probe_totalexcept httpx.HTTPStatusError→total=None→ тихий split/skip бакета. Теперь, при исчерпании новых бюджетов, surface какAvitoRateLimitedError(ловится раньше в_probe_total) → per-bucket tolerance. Net-эффект тот же (бакет скипается, partial preserved), но отказ теперь считается как per-bucket block, а не «тихий total=None».Тесты
tests/test_avito_browser_mode.py+5 тестов (секция #356): rotate-then-succeed, proxy-too-many exhausted →AvitoRateLimitedError, transient bounded (no infinite loop), 503 unavailable exhausted, mixed transient→soft-ban (независимые бюджеты).Deep-code-review: ✅ APPROVE (full propagation chain traced, termination proven, firewall-путь untouched).
Out of scope
Инфра не трогаю:
tradein-browserбылOOMKilled(лимит 5G), единый мобильный IP перегружен параллельным cian+avito+yandex трафиком (NS_ERROR_PROXY_TOO_MANY_REQUESTSв логах преимущественно Cian) — это отдельный proxy-pool / mem-limit follow-up.