fix(tradein/proxy): проба узла ходит браузерным трактом, вердикт живёт отдельно от HTTP-пробы (#2723) #2736
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
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#2736
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2723-healthcheck-real-path"
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?
Что не подтвердилось в постановке
Числа из issue воспроизвелись точно (прод,
scrape_runs), механизм — нет.checkedза суткиchecked=48в сутки при 48 прогонах = один узел за прогон. В те десять суток проба не «врала про пул» — она вообще не видела остальных узлов:scrape_proxiesid 10 и 11 созданы2026-07-31 12:59, а самовосстановление disabled-узлов (#2600 п.1) доехало 01.08 (до этого в counters нет ключейrevived/bans_purged).Главное: браузерный путь Авито подключён к пулу только 02.08 — PR #2637,
AvitoScraper.__init__(proxy_provider=...). До негоbuild_browser_fetcher(config, "avito")шёл без провайдера → в теле/fetchне былоproxy→ сайдкар брал env-SCRAPER_PROXY_URL(мёртвый mobileproxy, #2613). То есть проба и работа ходили через разные прокси, а не по разным трактам одного узла. Это и есть корень 90 обрывов, и он уже починен #2637/#2616 — отказы упали до нуля 04-06.08.Живая проверка сегодня (нейтральный адрес, нулевая нагрузка на площадки): все 4 узла проходят и HTTP-пробу, и браузерную, exit-IP совпадают. Узла в состоянии «HTTP да, браузер нет» сейчас не существует, воспроизвести исторический случай нечем.
Что при этом действительно сломано — и починено здесь
Пул теперь подключён (
USE_PROXY_POOL_BROWSER=true), и оба тракта пишут вердикт в один счётчик:/fetchчерез сайдкар репортитmark_health(ok=False)→consecutive_fails++;BrowserFetcherменяет lease после 3 подряд,acquireперестаёт выдавать узел приMAX_CONSECUTIVE_FAILS=3;consecutive_fails=0+enabled=true.Дешёвая проба стирает вердикт дорогого тракта. Узел, мёртвый для браузера, гарантированно возвращается в выдачу каждые полчаса и снова забирает прогон — вечный цикл, до порога
DISABLE_THRESHOLD=5он не дойдёт никогда. Это ровно #2686 (два диагноза в одном флаге) вconsecutive_fails.Как разведены диагнозы
Приём #2711 — поле рядом, а не новое значение существующего флага. Миграция 228:
browser_fail_streak,browser_unfit_since,browser_check_at.consecutive_fails,enabled,exit_ipbrowser_*Пересечения нет ни в одну сторону: успешная ipify не обнуляет
browser_fail_streak, провал браузерной пробы не трогаетenabled/consecutive_fails.Пометка не выводит узел из пула. При 4 узлах (#2638) голодание хуже работы через плохой узел — та же политика, что у защиты последнего узла в
mark_banned.acquireлишь отдаёт непригодный узел последним (ORDER BY (browser_unfit_since IS NOT NULL), …) и громко логирует, если пригодных не осталось. Путь обратно — успешная браузерная проба (browser_refit), плюс реальный трафик, когда узел всё-таки выдан.Чужой отказ узлу не засчитывается.
classify_browser_probe→proxy(503browser unavailable (proxy may be down)/ 500NS_ERROR_PROXY_*) считается;sidecar(сайдкар лежит,no proxy configured) иpage— нет. Иначе одна упавшая общая зависимость пометила бы непригодным весь пул разом.Адрес и тракт пробы
POST /fetch→https://www.avito.ru/robots.txt, прокси узла в теле,source='generic'./fetch, а не/fetch-json: последний сначала делаетgotoна главную площадки — это уже заметная нагрузка, ради избегания которой проба и затевалась.source='generic': сайдкар держит по инстансу camoufox на провайдера со своим локом. Проба сsource='avito'забирала бы лок боевого инстанса и релончила его (прокси пробы ≠ прокси сессии) — ровно тот relaunch-шторм, который лечил sticky-lease фикс.Стоимость — по числам, не по ощущению (замер на проде 06.08)
Одна браузерная проба = 8.3 с и один запуск camoufox (замерено вживую, включая relaunch). Гнать её каждые 30 минут = удвоить самую дорогую операцию сайдкара ради диагностики. Такт 360 мин + два сокращения: проба идёт только по узлам, прошедшим ipify (не прошедший мёртв целиком, диагноз уже есть), и только при
use_proxy_pool_browser(иначе браузер ходит мимо пула). Неподтверждённый провал такт не двигает → второе подтверждение приходит на следующем прогоне (~30 мин), а не через 6 часов.Test plan
tests/test_2723_browser_probe.py— 20 тестов: классификация отказа (7 кейсов), тракт пробы (эндпоинт/адрес/прокси в теле), неразрушение вердикта, обе стороны разведения, лежащий сайдкар не винит никого, порядок выдачи, «все непригодны → прокси всё равно выдаётся», реанимация, такт, оба гейта.mark_health(ok=True)обнуляет и браузерный вердикт) → красныеtest_ipify_success_does_not_erase_browser_verdict,test_healthcheck_marks_unfit_when_http_green_browser_red,test_healthcheck_revives_unfit_node. Наorigin/mainфайл не импортируется (нет ни функции, ни констант).FakeSessionрасширен так, что «починить» боевой SQL за него нельзя: новые ветки гейтятся по подстрокам самого запроса.ruff check/ruff formatчисто по изменённым файлам (3 ошибки вtest_estimator_pure_units.py— pre-existing, воспроизводятся наorigin/main).deploy-tradein.ymlчерезpackages/scraper-kit/**— без этогоproxy_pool.py(app/services/**в allowlist нет) уехал бы в образ, ноtradein-scraper, где и крутится healthcheck, остался бы на старом коде.Прод-верификация после мержа
Маркер — новые ключи в counters:
SELECT counters FROM scrape_runs WHERE source='proxy_healthcheck' ORDER BY id DESC LIMIT 1→ должны появитьсяbrowser_checked/browser_ok/browser_unfit/browser_refit. Первый прогон после деплоя дастbrowser_checked=4(у всех узловbrowser_check_at IS NULL).Refs #2723