tradein/browser: cian-инстанс camoufox отказывает в 63% запусков (99/156 за 30ч) — релонч браузера перед КАЖДЫМ fetch из-за чередования аренд; generic на том же узле 0% #3412

Open
opened 2026-09-07 11:10:25 +00:00 by bot-backend · 1 comment
Collaborator

Найдено при диагностике #3411 (прод, 07.09, BUILD_SHA=f52a39e). Отказывает не площадка, а наш инстанс браузера.

Решающая улика — один узел (14), один URL, одна минута:

browser (generic) -> SIDECAR 200, upstream 200, 880 687 Б, 12.1 с, title='ЖК «Мкр. Центральный» ...'
browser (cian)    -> SIDECAR 500, 61.6 с, {"error":"TimeoutError: Page.goto: Timeout 60000ms exceeded"}  x3
curl (тот же прокси) -> 200, 604 035 Б, 1.35 с, redirects=0

Масштаб: tradein-browser[cian]99 ошибок на 156 запусков camoufox (63%) за 30 ч; tradein-browser[generic]0. Таймаутов Page.goto: 87 на ekb.cian.ru (обычные карточки) + 5 на zhk-*. Болен весь циановский тракт; detail-бэкфилл выживает за счёт ретраев (399/400 достаётся дорого), малообъёмная фаза houses ложится целиком.

Первый подозреваемый — релонч перед каждым запросом. В логе буквально перед каждым fetch:

tradein-browser[cian]: прокси изменился (override=True) — relaunch перед fetch
tradein-browser[cian]: браузер закрыт -> запуск AsyncCamoufox (proxy=True, recycle_pages=15) -> браузер запущен

У сайдкара один инстанс на провайдера (_locks[provider], BROWSER_CONCURRENCY=1), а cian-вызывающих несколько, каждый со своей арендой (proxy_override) -> _ensure_browser видит смену прокси -> _close_browser + холодный старт. Холодный camoufox под domcontentloaded-навигацию тяжёлой страницы Циана не доезжает за 60 с. recycle_pages=15 не работает: инстанс не живёт до 15 страниц.

Варианты (нужен замер, не догадка): инстанс на пару (provider, proxy) вместо (provider); «липкая» аренда для cian на окно прогона (как #2640 для другого пути); очередь-батчинг по провайдеру, чтобы подряд идущие запросы одной аренды не перезапускали браузер. Плюс проверить необходимость прогрева после старта.

Приёмка: доля browser launch failed / Page.goto timeout у [cian] за сутки < 10 % (сейчас 63 %); число строк «relaunch перед fetch» падает кратно; медиана времени fetch по cian ~ времени generic (10-12 с).

Refs #3411, #3402, #3386, #2640.

Найдено при диагностике #3411 (прод, 07.09, `BUILD_SHA=f52a39e`). **Отказывает не площадка, а наш инстанс браузера.** Решающая улика — один узел (14), один URL, одна минута: ``` browser (generic) -> SIDECAR 200, upstream 200, 880 687 Б, 12.1 с, title='ЖК «Мкр. Центральный» ...' browser (cian) -> SIDECAR 500, 61.6 с, {"error":"TimeoutError: Page.goto: Timeout 60000ms exceeded"} x3 curl (тот же прокси) -> 200, 604 035 Б, 1.35 с, redirects=0 ``` **Масштаб:** `tradein-browser[cian]` — **99 ошибок на 156 запусков camoufox (63%)** за 30 ч; `tradein-browser[generic]` — **0**. Таймаутов `Page.goto`: 87 на `ekb.cian.ru` (обычные карточки) + 5 на `zhk-*`. Болен весь циановский тракт; detail-бэкфилл выживает за счёт ретраев (399/400 достаётся дорого), малообъёмная фаза houses ложится целиком. **Первый подозреваемый — релонч перед каждым запросом.** В логе буквально перед каждым fetch: ``` tradein-browser[cian]: прокси изменился (override=True) — relaunch перед fetch tradein-browser[cian]: браузер закрыт -> запуск AsyncCamoufox (proxy=True, recycle_pages=15) -> браузер запущен ``` У сайдкара один инстанс на провайдера (`_locks[provider]`, `BROWSER_CONCURRENCY=1`), а cian-вызывающих несколько, каждый со своей арендой (`proxy_override`) -> `_ensure_browser` видит смену прокси -> `_close_browser` + холодный старт. Холодный camoufox под `domcontentloaded`-навигацию тяжёлой страницы Циана не доезжает за 60 с. `recycle_pages=15` не работает: инстанс не живёт до 15 страниц. **Варианты (нужен замер, не догадка):** инстанс на пару (provider, proxy) вместо (provider); «липкая» аренда для cian на окно прогона (как #2640 для другого пути); очередь-батчинг по провайдеру, чтобы подряд идущие запросы одной аренды не перезапускали браузер. Плюс проверить необходимость прогрева после старта. **Приёмка:** доля `browser launch failed` / `Page.goto timeout` у `[cian]` за сутки < 10 % (сейчас 63 %); число строк «relaunch перед fetch» падает кратно; медиана времени fetch по cian ~ времени generic (10-12 с). Refs #3411, #3402, #3386, #2640.
Author
Collaborator

Поправка к постановке: цифра «63 %» в заголовке этого issue неверна — она моя, и она артефакт знаменателя. Независимый замер (двумя агентами, сошлись до единицы, окно 06.09 10:09 → 07.09 11:12, только чтение):

  • 99 ошибок делились на 156 запусков браузера, а не на запросы. На запрос: POST /fetch = 2377×200, 112×500, 96×403, 2×503; у cian 99 ошибок (95 TimeoutError) на ~2000 фетчей = ~4.8 %.
  • «Релонч перед КАЖДЫМ fetch, recycle_pages=15 не срабатывает ни разу» — опровергнуто: из 154 успешных запусков 104 (67 %) по recycle-порогу, 49 (32 %) по смене аренды. Сам запуск дешёвый: медиана 0.6 с.
  • Прямая фальсификация: в час, когда релонч действительно шёл перед каждым фетчем (24 смены аренды на 25 фетчей), доля отказов 1/26 = 3.8 % — НИЖЕ суточной.
  • Холодный старт всё же хуже прогретого, но объясняет меньшинство: 19 из 99 отказов — первый фетч на свежем инстансе (12.3 % против 4.2 % на прогретом, концентрация 2.9×, не 20×). Остальные 80 — 86 разных URL, 80 падают ровно один раз, все часы суток, на прогретых инстансах.

Что замер нашёл вместо этого — и это хуже: инстансы camoufox никогда не закрывались (_close_browser звался только на relaunch/crash/shutdown), поставщиков пять. Прод: 4 живых процесса = 1.35 ГиБ RSS, memory.current 2.01 ГиБ при лимите 2560 МиБ (80 %), ~425 МиБ на инстанс. Cgroup за те же 25 ч: oom_kill=111, memory.events max=40781, sock_throttled=22177, плюс 25 крашей браузера (TargetClosedError: avito 20, domclick 5) и один инстанс, помеченный живым в логе, но отсутствующий в ps.

Так что задача переопределяется: потолок живых инстансов и корректное вытеснение, а не «убрать релонч». Ветка fix/3412-cian-instance-relaunch (пул по паре provider+proxy, потолок 4, LRU) получила от ревью major — четыре дефекта, каждый воспроизведён пробой:

  1. _pick_evictable слеп к цене жертвы: закрывает ЖИВОЙ avito с якорной вкладкой, простаивающий припаркованный cian оставляет.
  2. _last_goto_at.pop при вытеснении сбрасывает пейсинг ЧУЖОГО поставщика (avito 8 с / cian 18 с / domclick 12 с) → следующий фетч без паузы → риск бана.
  3. retry-путь _try_launch_browser поднимает инстанс мимо _trim_instances → живых 3 при потолке 2 (425 МиБ × N сверх лимита).
  4. _launch_browser не зовёт _touch_instance → свежий инстанс с seq=0 становится мгновенной LRU-жертвой.

И ключевое наблюдение ревьюера, меняющее приоритет: reuse_context=True шлют только avito- и domclick-бэкфиллы, cian — никогда. У припаркованного cian-инстанса context=None, anchor_page=None, то есть пул греет для cian только процесс (49 × 0.6 с ≈ 30 с в сутки), а жертвой вытеснения становится avito с якорем, чья цена 4-5 с против 17-52 с на карточку. Чинить надо в первую очередь выбор жертвы, а не пул.

Оставшиеся ~80 таймаутов (тяжёлый хвост навигации Циана на прогретых инстансах) кодом сайдкара вслепую не чинятся — вынесено отдельно.

**Поправка к постановке: цифра «63 %» в заголовке этого issue неверна — она моя, и она артефакт знаменателя.** Независимый замер (двумя агентами, сошлись до единицы, окно 06.09 10:09 → 07.09 11:12, только чтение): - 99 ошибок делились на **156 запусков браузера**, а не на запросы. На запрос: `POST /fetch` = 2377×200, 112×500, 96×403, 2×503; у cian 99 ошибок (95 `TimeoutError`) на ~2000 фетчей = **~4.8 %**. - «Релонч перед КАЖДЫМ fetch, `recycle_pages=15` не срабатывает ни разу» — **опровергнуто**: из 154 успешных запусков 104 (67 %) по recycle-порогу, 49 (32 %) по смене аренды. Сам запуск дешёвый: медиана 0.6 с. - Прямая фальсификация: в час, когда релонч действительно шёл перед каждым фетчем (24 смены аренды на 25 фетчей), доля отказов **1/26 = 3.8 %** — НИЖЕ суточной. - Холодный старт всё же хуже прогретого, но объясняет меньшинство: 19 из 99 отказов — первый фетч на свежем инстансе (12.3 % против 4.2 % на прогретом, концентрация 2.9×, не 20×). Остальные 80 — 86 разных URL, 80 падают ровно один раз, все часы суток, на прогретых инстансах. **Что замер нашёл вместо этого — и это хуже:** инстансы camoufox **никогда не закрывались** (`_close_browser` звался только на relaunch/crash/shutdown), поставщиков пять. Прод: 4 живых процесса = 1.35 ГиБ RSS, `memory.current` **2.01 ГиБ при лимите 2560 МиБ (80 %)**, ~425 МиБ на инстанс. Cgroup за те же 25 ч: `oom_kill=111`, `memory.events max=40781`, `sock_throttled=22177`, плюс 25 крашей браузера (`TargetClosedError`: avito 20, domclick 5) и один инстанс, помеченный живым в логе, но отсутствующий в `ps`. Так что задача переопределяется: **потолок живых инстансов и корректное вытеснение**, а не «убрать релонч». Ветка `fix/3412-cian-instance-relaunch` (пул по паре provider+proxy, потолок 4, LRU) получила от ревью ❌ **major** — четыре дефекта, каждый воспроизведён пробой: 1. `_pick_evictable` слеп к цене жертвы: закрывает ЖИВОЙ avito с якорной вкладкой, простаивающий припаркованный cian оставляет. 2. `_last_goto_at.pop` при вытеснении сбрасывает пейсинг ЧУЖОГО поставщика (avito 8 с / cian 18 с / domclick 12 с) → следующий фетч без паузы → риск бана. 3. retry-путь `_try_launch_browser` поднимает инстанс мимо `_trim_instances` → живых 3 при потолке 2 (425 МиБ × N сверх лимита). 4. `_launch_browser` не зовёт `_touch_instance` → свежий инстанс с `seq=0` становится мгновенной LRU-жертвой. И ключевое наблюдение ревьюера, меняющее приоритет: `reuse_context=True` шлют только avito- и domclick-бэкфиллы, **cian — никогда**. У припаркованного cian-инстанса `context=None`, `anchor_page=None`, то есть пул греет для cian только процесс (49 × 0.6 с ≈ 30 с в сутки), а жертвой вытеснения становится avito с якорем, чья цена 4-5 с против 17-52 с на карточку. Чинить надо в первую очередь **выбор жертвы**, а не пул. Оставшиеся ~80 таймаутов (тяжёлый хвост навигации Циана на прогретых инстансах) кодом сайдкара вслепую не чинятся — вынесено отдельно.
Sign in to join this conversation.
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#3412
No description provided.