tradein: пул прокси на пути /estimate — синхронный acquire без statement_timeout на event loop, IMV-баны атрибутируются источнику avito целиком, house_imv_backfill curl-путь без … #3398

Open
opened 2026-09-06 05:45:43 +00:00 by bot-backend · 6 comments
Collaborator

Хвосты ревью PR #3397 (#3386 п.в), не блокеры:

  1. Зависание вне бюджета. curl_proxy_url входит синхронно до первого await (imv.py:~533), поэтому _with_budget(estimate_avito_imv_timeout_s) не может прервать acquire; в proxy_pool.acquire нет lock_timeout/statement_timeout (SKIP LOCKED есть — на блокировке строки не ждёт, но на checkout коннекта при исчерпанном пуле 5+10 ждёт до 30 с, держа event loop). Цена на cache-miss: 3 сессии (acquire / mark_health / release), ~5-7 стейтментов синхронно. Варианты: SET LOCAL statement_timeout в acquire, либо asyncio.to_thread вокруг входа в контекст (нужна смена сигнатуры kit). Refs #3083.
  2. Атрибуция банов. _proxy.py:~132 при ProxyBanError зовёт mark_banned(lease, source="avito") — IMV-ручка (XHR, другая анти-бот поверхность) теперь банит узлы для всего источника avito, включая detail-бэкфилл; /estimate — пользовательский трафик и начнёт доминировать в атрибуции. Наблюдать прирост scrape_proxy_source_bans c source='avito' в корреляции с /estimate; вариант «отдельный source avito_imv» ломает affinity при выдаче — нужна правка acquire. Решение владельца.
  3. app/services/house_imv_backfill.py:~929 при browser_fetcher=None уходит в curl-путь evaluate_via_imv без proxy_provider — тот же мёртвый env-фолбэк; чинить там, где сходятся все вызовы (evaluate_via_imv с обязательным провайдером в production?).

Refs #3386, PR #3397, #3083, #2613.

Хвосты ревью PR #3397 (#3386 п.в), не блокеры: 1. **Зависание вне бюджета.** `curl_proxy_url` входит синхронно до первого `await` (`imv.py:~533`), поэтому `_with_budget(estimate_avito_imv_timeout_s)` не может прервать acquire; в `proxy_pool.acquire` нет `lock_timeout`/`statement_timeout` (`SKIP LOCKED` есть — на блокировке строки не ждёт, но на checkout коннекта при исчерпанном пуле 5+10 ждёт до 30 с, держа event loop). Цена на cache-miss: 3 сессии (acquire / mark_health / release), ~5-7 стейтментов синхронно. Варианты: `SET LOCAL statement_timeout` в acquire, либо `asyncio.to_thread` вокруг входа в контекст (нужна смена сигнатуры kit). Refs #3083. 2. **Атрибуция банов.** `_proxy.py:~132` при `ProxyBanError` зовёт `mark_banned(lease, source="avito")` — IMV-ручка (XHR, другая анти-бот поверхность) теперь банит узлы для всего источника `avito`, включая detail-бэкфилл; `/estimate` — пользовательский трафик и начнёт доминировать в атрибуции. Наблюдать прирост `scrape_proxy_source_bans` c `source='avito'` в корреляции с `/estimate`; вариант «отдельный source `avito_imv`» ломает affinity при выдаче — нужна правка acquire. Решение владельца. 3. `app/services/house_imv_backfill.py:~929` при `browser_fetcher=None` уходит в curl-путь `evaluate_via_imv` без `proxy_provider` — тот же мёртвый env-фолбэк; чинить там, где сходятся все вызовы (`evaluate_via_imv` с обязательным провайдером в production?). Refs #3386, PR #3397, #3083, #2613.
Author
Collaborator

Ещё один источник /estimate на мёртвом фолбэке (прод, 06.09 06:13 UTC, сырой лог backend при пробе /estimate):

curl_cffi.requests.exceptions.ProxyError: Failed to perform, curl: (56) CONNECT tunnel failed, response 407.
… INFO app.services.estimator: yandex_valuation: empty result for address=21/1, улица Готвальда, ЖК "Татлин", …

estimator.py:~995YandexValuationScraper(RealScraperConfig(), delay_provider=…) без proxy_provider (ревью #3387 это отмечало) → curl через SCRAPER_PROXY_URL (узел 9, 407) → «empty result», который читается как «Яндекс ничего не знает об адресе», а не как отказ прокси. Та же история, что с IMV в PR #3397; estimate_via_cian_valuation (estimator.py:~4603) — проверить тем же способом. Чинить единообразно с #3397 (провайдер + честный лог при пустом пуле/407).

**Ещё один источник `/estimate` на мёртвом фолбэке (прод, 06.09 06:13 UTC, сырой лог backend при пробе `/estimate`):** ``` curl_cffi.requests.exceptions.ProxyError: Failed to perform, curl: (56) CONNECT tunnel failed, response 407. … INFO app.services.estimator: yandex_valuation: empty result for address=21/1, улица Готвальда, ЖК "Татлин", … ``` `estimator.py:~995` — `YandexValuationScraper(RealScraperConfig(), delay_provider=…)` без `proxy_provider` (ревью #3387 это отмечало) → curl через `SCRAPER_PROXY_URL` (узел 9, 407) → «empty result», который читается как «Яндекс ничего не знает об адресе», а не как отказ прокси. Та же история, что с IMV в PR #3397; `estimate_via_cian_valuation` (`estimator.py:~4603`) — проверить тем же способом. Чинить единообразно с #3397 (провайдер + честный лог при пустом пуле/407).
Author
Collaborator

Подтверждено и для cian: на каждой пробе /estimate (06:13 и 06:14 UTC) — WARNING scraper_kit.providers.cian.valuation: Cian valuation fetch failed: … CONNECT tunnel failed, response 407 рядом с yandex_valuation: empty result. Оба внешних источника оценки мертвы на env-фолбэке с 02.09; в ответе это выглядит как «нет данных», а не как отказ прокси. Фикс обоих путей — в работе по образцу #3397.

Подтверждено и для cian: на каждой пробе `/estimate` (06:13 и 06:14 UTC) — `WARNING scraper_kit.providers.cian.valuation: Cian valuation fetch failed: … CONNECT tunnel failed, response 407` рядом с `yandex_valuation: empty result`. Оба внешних источника оценки мертвы на env-фолбэке с 02.09; в ответе это выглядит как «нет данных», а не как отказ прокси. Фикс обоих путей — в работе по образцу #3397.
Author
Collaborator

П. 4 (новое, по пробе #3397 06.09 06:15 UTC): живой IMV из /estimate через curl отбивается Авито 403 на warm-up (GET /evaluation/realty) независимо от узла (datacenter-защита, #562) — путь мёртв структурно; якорь в ответе даёт house-level IMV бэкфилла (imv_anchor, estimator.py:3357, приоритет над imv_eval). После #3397 каждый cache-miss с house_type/repair_state берёт lease и пишет mark_health ok=False исправному узлу за отказ площадки (не инфраструктуры) → consecutive_fails растёт от пользовательского трафика. Варианты (решение владельца): (а) в /estimate использовать браузерный фетчер при avito_imv_use_browser_fetcher (как бэкфилл), (б) не звать живой IMV, когда есть house-level якорь (сейчас зовётся всегда при cache-miss), (в) 403 на IMV-warm-up не считать нездоровьем узла (fail_kind/health=False как в #3288 для бан-страниц).

**П. 4 (новое, по пробе #3397 06.09 06:15 UTC):** живой IMV из `/estimate` через curl отбивается Авито **403 на warm-up** (`GET /evaluation/realty`) независимо от узла (datacenter-защита, #562) — путь мёртв структурно; якорь в ответе даёт house-level IMV бэкфилла (`imv_anchor`, `estimator.py:3357`, приоритет над `imv_eval`). После #3397 каждый cache-miss с `house_type`/`repair_state` берёт lease и пишет `mark_health ok=False` исправному узлу за отказ площадки (не инфраструктуры) → `consecutive_fails` растёт от пользовательского трафика. Варианты (решение владельца): (а) в `/estimate` использовать браузерный фетчер при `avito_imv_use_browser_fetcher` (как бэкфилл), (б) не звать живой IMV, когда есть house-level якорь (сейчас зовётся всегда при cache-miss), (в) 403 на IMV-warm-up не считать нездоровьем узла (`fail_kind`/`health=False` как в #3288 для бан-страниц).
Author
Collaborator

Дополнение по ревью #3399 (не в этом PR): providers/yandex/valuation.py:219-230await self._cffi_session.close() стоит ДО закрытия proxy-стека и без try/finally: исключение из close() оставит lease до reap_stale_leases (30 мин); путь теперь активирован публичным /estimate. И повтор п.1: scraper_adapters acquire/release/mark_health синхронно из корутины при 1 воркере uvicorn — теперь на горячем пути три источника (IMV, yandex, cian).

Дополнение по ревью #3399 (не в этом PR): `providers/yandex/valuation.py:219-230` — `await self._cffi_session.close()` стоит ДО закрытия proxy-стека и без `try/finally`: исключение из `close()` оставит lease до `reap_stale_leases` (30 мин); путь теперь активирован публичным `/estimate`. И повтор п.1: `scraper_adapters` acquire/release/mark_health синхронно из корутины при 1 воркере uvicorn — теперь на горячем пути три источника (IMV, yandex, cian).
Author
Collaborator

Приёмка PR #3399 на проде (06.09 07:15 UTC, деплой bbf38f5, маркер «пул прокси пуст — кэш не прогрет» в контейнере — есть). Проба POST /api/v1/trade-in/estimate изнутри контейнера (адрес без кэша), 200 за 8.5 с:

yandex_valuation: cache MISS key=ff311863 — fetch отложен
Cian valuation: cache MISS key=3f4a6e05… — fetch отложен
proxy_pool: leased proxy id=1 provider=yandex by=-1
proxy_pool: leased proxy id=13 provider=cian by=-1
proxy_pool: mark_health id=13 ok=True … released proxy id=13
Cian valuation extracted: sale=9900000.0 (acc=93.0%) rent=22000.0
proxy_pool: mark_health id=1 ok=True … released proxy id=1
yandex_valuation: fresh fetch saved key=ff311863 items=20

Строк 407 CONNECT tunnel failed — 0 (единственное совпадение «407» за окно — порт 40784 в строке /metrics). Оба внешних источника оценки снова приносят данные (с 02.09 — только 407); lease взят и освобождён на каждом, ok=True. Открытыми в issue остаются п. 1 (sync acquire на event loop), п. 2 (атрибуция банов), п. 4 (живой IMV → 403 на warm-up), yandex/valuation.py порядок close().

**Приёмка PR #3399 на проде (06.09 07:15 UTC, деплой `bbf38f5`, маркер «пул прокси пуст — кэш не прогрет» в контейнере — есть).** Проба `POST /api/v1/trade-in/estimate` изнутри контейнера (адрес без кэша), 200 за 8.5 с: ``` yandex_valuation: cache MISS key=ff311863 — fetch отложен Cian valuation: cache MISS key=3f4a6e05… — fetch отложен proxy_pool: leased proxy id=1 provider=yandex by=-1 proxy_pool: leased proxy id=13 provider=cian by=-1 proxy_pool: mark_health id=13 ok=True … released proxy id=13 Cian valuation extracted: sale=9900000.0 (acc=93.0%) rent=22000.0 proxy_pool: mark_health id=1 ok=True … released proxy id=1 yandex_valuation: fresh fetch saved key=ff311863 items=20 ``` Строк `407 CONNECT tunnel failed` — 0 (единственное совпадение «407» за окно — порт `40784` в строке `/metrics`). Оба внешних источника оценки снова приносят данные (с 02.09 — только 407); lease взят и освобождён на каждом, `ok=True`. Открытыми в issue остаются п. 1 (sync acquire на event loop), п. 2 (атрибуция банов), п. 4 (живой IMV → 403 на warm-up), `yandex/valuation.py` порядок `close()`.
Author
Collaborator

П. 1 закрыт (PR #3407, деплой f52a39e 06.09 ~12:00 UTC; маркер acurl_proxy_url в _proxy.py контейнера backend). Проба /estimate после деплоя: 200 за 0.5 с (sync-часть), фоновые догрузки — leased proxy id=1 provider=yandex / id=13 provider=cianCian valuation extracted: sale=11700000 (acc=95 %), yandex_valuation: fresh fetch saved items=13, оба lease освобождены; QueuePool/трейсбеков — 0. Пул-операции ушли с event loop; потолки (пул коннектов 15 vs 28, бюджет на ветке acquire, sync db.execute в estimator, админ-ручка cian) — #3408.

**П. 1 закрыт (PR #3407, деплой `f52a39e` 06.09 ~12:00 UTC; маркер `acurl_proxy_url` в `_proxy.py` контейнера backend).** Проба `/estimate` после деплоя: 200 за 0.5 с (sync-часть), фоновые догрузки — `leased proxy id=1 provider=yandex` / `id=13 provider=cian` → `Cian valuation extracted: sale=11700000 (acc=95 %)`, `yandex_valuation: fresh fetch saved items=13`, оба lease освобождены; `QueuePool`/трейсбеков — 0. Пул-операции ушли с event loop; потолки (пул коннектов 15 vs 28, бюджет на ветке acquire, sync `db.execute` в estimator, админ-ручка cian) — #3408.
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#3398
No description provided.