tradein-backend без USE_PROXY_POOL_* — браузерные скрейпы из backend (yandex full-load, админ-ручки) идут на SCRAPER_PROXY_URL = выключенный узел 9 (407) → camoufox InvalidIP, 83 провала … #3386

Closed
opened 2026-09-05 22:10:39 +00:00 by bot-backend · 6 comments
Collaborator

Факт (Poincare, 2026-09-05 22:00 UTC). Проба POST /api/v1/admin/scrape/yandex-full-load (run 6153, приёмка #3375) дала 18 бакетов partial с unique_fetched=0. В логе сайдкара — tradein-browser[yandex]: browser launch failed: proxy unreachable (exc_type=InvalidIP)/fetch 503; 83 таких провала за 3 часа. Backend-логов аренды для yandex нет вовсе.

Цепочка (проверена рабочим трактом, не рассуждением):

  1. docker exec tradein-backend printenv | grep PROXY → только SCRAPER_PROXY_URL=…@190.2.145.131:10313; USE_PROXY_POOL_BROWSER/CURL отсутствуют. У tradein-scraper они есть (docker-compose.prod.yml:385-386, environment: сервиса scraper, .env.runtime их не содержит).
  2. admin._kit_proxy_provider() при обоих флагах False → None; RealScraperConfig().use_proxy_pool_browser=Falsebuild_browser_fetcher без пула → /fetch без proxy в теле → сайдкар берёт env-фолбэк SCRAPER_PROXY_URL.
  3. 190.2.145.131:10313 = узел 9 пула (scrape_proxies: enabled=false, consecutive_fails=82, ASocks). Прямой вызов того, на чём падает сайдкар: camoufox.ip.public_ip(SCRAPER_PROXY_URL)InvalidIP: … Tunnel connection failed: 407 Proxy Authentication Required; тот же вызов с узлом 1 (affinity yandex) → 194.32.229.229 за 0.8 с. Узлы 1/13/14 из контейнера сайдкара живые (ipify и realty.yandex.ru — 200).
  4. Отказ прокси приходит клиенту как 503 «browser unavailable (proxy may be down)» — classify_browser_probe знает маркер, но на fetch-пути без аренды считать нечего: узлу пула ничего не пишется, ротации нет, каждый запрос — 503.

Следствия. Любой браузерный путь из процесса backend в проде мёртв или живёт случайно: yandex full-load (#3375 не проверить по эффекту, #3366), админские debug-роуты, а IMV-путь оценщика (house_imv_backfill.py:749, avito /fetch-json) работает только потому, что сайдкар для avito уже поднят scraper'ом с его арендой (_ensure_browser(proxy_override=None) держит текущий инстанс) — связь по случайности. Это класс #2634 («защита мертва, пока env-признак не доезжает до всех путей»).

Чинить. (а) Те же два флага сервису backend в tradein-mvp/docker-compose.prod.yml (одним PR; проверить blast radius: IMV-путь начнёт брать свою аренду на каждую оценку — нужен взгляд ревью на house_imv_backfill/avito/imv.py); (б) SCRAPER_PROXY_URL как env-фолбэк указывает на выключенный узел — либо снять фолбэк в проде вовсе (#2616 шаг 2 уже режет его при пустом пуле), либо владельцу перевести на живой; (в) yandex-affinity узел ровно один (id 1) — при его бане Яндекс остаётся без пула.

Приёмка. После деплоя: docker exec tradein-backend printenv USE_PROXY_POOL_BROWSER = true; повтор full-load пробы — в логе сайдкара для yandex прокси изменился (override=True) или запуск без InvalidIP, unique_fetched > 0 за первые бакеты (сама проба — coop-cancel через 2-3 бакета); InvalidIP в docker logs tradein-browser за час после — 0.

Refs #3375, #3366, #3197 (точки 2-3 — yandex newbuilding — на том же мёртвом фолбэке), #2616, #2634.

**Факт (Poincare, 2026-09-05 22:00 UTC).** Проба `POST /api/v1/admin/scrape/yandex-full-load` (run 6153, приёмка #3375) дала 18 бакетов `partial` с `unique_fetched=0`. В логе сайдкара — `tradein-browser[yandex]: browser launch failed: proxy unreachable (exc_type=InvalidIP)` → `/fetch 503`; **83 таких провала за 3 часа**. Backend-логов аренды для yandex нет вовсе. **Цепочка (проверена рабочим трактом, не рассуждением):** 1. `docker exec tradein-backend printenv | grep PROXY` → только `SCRAPER_PROXY_URL=…@190.2.145.131:10313`; `USE_PROXY_POOL_BROWSER/CURL` **отсутствуют**. У `tradein-scraper` они есть (`docker-compose.prod.yml:385-386`, `environment:` сервиса scraper, `.env.runtime` их не содержит). 2. `admin._kit_proxy_provider()` при обоих флагах False → `None`; `RealScraperConfig().use_proxy_pool_browser=False` → `build_browser_fetcher` без пула → `/fetch` без `proxy` в теле → сайдкар берёт env-фолбэк `SCRAPER_PROXY_URL`. 3. `190.2.145.131:10313` = узел **9** пула (`scrape_proxies`: `enabled=false`, `consecutive_fails=82`, ASocks). Прямой вызов того, на чём падает сайдкар: `camoufox.ip.public_ip(SCRAPER_PROXY_URL)` → `InvalidIP: … Tunnel connection failed: 407 Proxy Authentication Required`; тот же вызов с узлом 1 (affinity yandex) → `194.32.229.229` за 0.8 с. Узлы 1/13/14 из контейнера сайдкара живые (ipify и realty.yandex.ru — 200). 4. Отказ прокси приходит клиенту как 503 «browser unavailable (proxy may be down)» — `classify_browser_probe` знает маркер, но на fetch-пути без аренды считать нечего: узлу пула ничего не пишется, ротации нет, каждый запрос — 503. **Следствия.** Любой браузерный путь из процесса backend в проде мёртв или живёт случайно: yandex full-load (#3375 не проверить по эффекту, #3366), админские debug-роуты, а IMV-путь оценщика (`house_imv_backfill.py:749`, avito `/fetch-json`) работает только потому, что сайдкар для avito уже поднят scraper'ом с его арендой (`_ensure_browser(proxy_override=None)` держит текущий инстанс) — связь по случайности. Это класс #2634 («защита мертва, пока env-признак не доезжает до всех путей»). **Чинить.** (а) Те же два флага сервису backend в `tradein-mvp/docker-compose.prod.yml` (одним PR; проверить blast radius: IMV-путь начнёт брать свою аренду на каждую оценку — нужен взгляд ревью на `house_imv_backfill`/`avito/imv.py`); (б) `SCRAPER_PROXY_URL` как env-фолбэк указывает на выключенный узел — либо снять фолбэк в проде вовсе (#2616 шаг 2 уже режет его при пустом пуле), либо владельцу перевести на живой; (в) yandex-affinity узел ровно один (id 1) — при его бане Яндекс остаётся без пула. **Приёмка.** После деплоя: `docker exec tradein-backend printenv USE_PROXY_POOL_BROWSER` = true; повтор full-load пробы — в логе сайдкара для yandex `прокси изменился (override=True)` или запуск без InvalidIP, `unique_fetched > 0` за первые бакеты (сама проба — coop-cancel через 2-3 бакета); `InvalidIP` в `docker logs tradein-browser` за час после — 0. Refs #3375, #3366, #3197 (точки 2-3 — yandex newbuilding — на том же мёртвом фолбэке), #2616, #2634.
Author
Collaborator

PR #3387 смержен (06.09 ~22:35 UTC, main 68759927), деплой + проба full-load с coop-cancel — под слежением, результат допишу сюда.

Хвосты, оставшиеся после п. (а) — по ревью #3387:

  • п. (в), IMV оценщика: estimator.py:786-796/819-829 зовут evaluate_via_imv(..., config=RealScraperConfig()) без proxy_provider → флаг на /estimate не действует по построению (use_pool = флаг AND provider is not None); IMV продолжает ходить на SCRAPER_PROXY_URL (мёртвый узел 9) и живёт, только пока сайдкар avito поднят scraper'ом с его арендой. Нужен proxy_provider=RealProxyProvider() в этих двух вызовах + решение, брать ли lease на каждую оценку (или переиспользовать сайдкар без override, как сейчас де-факто).
  • cian_price_history.py:37-53 _PoolCurlConfig форсил use_proxy_pool_curl=True именно из-за отсутствия env у backend — теперь избыточен, докстринг неверен; снять отдельной правкой.
  • yandex-affinity узел в пуле ровно один (id 1): при его бане Яндекс остаётся без пула (узлы 13/14 — any, годятся ли Яндексу по affinity-правилу — проверить в proxy_pool.acquire).
  • SCRAPER_PROXY_URL в .env.runtime указывает на выключенный узел — решение владельца: снять фолбэк или перевести на живой.
PR #3387 смержен (06.09 ~22:35 UTC, main `68759927`), деплой + проба full-load с coop-cancel — под слежением, результат допишу сюда. Хвосты, оставшиеся после п. (а) — по ревью #3387: - **п. (в), IMV оценщика:** `estimator.py:786-796/819-829` зовут `evaluate_via_imv(..., config=RealScraperConfig())` без `proxy_provider` → флаг на `/estimate` не действует по построению (`use_pool = флаг AND provider is not None`); IMV продолжает ходить на `SCRAPER_PROXY_URL` (мёртвый узел 9) и живёт, только пока сайдкар avito поднят scraper'ом с его арендой. Нужен `proxy_provider=RealProxyProvider()` в этих двух вызовах + решение, брать ли lease на каждую оценку (или переиспользовать сайдкар без override, как сейчас де-факто). - `cian_price_history.py:37-53` `_PoolCurlConfig` форсил `use_proxy_pool_curl=True` именно из-за отсутствия env у backend — теперь избыточен, докстринг неверен; снять отдельной правкой. - yandex-affinity узел в пуле ровно один (id 1): при его бане Яндекс остаётся без пула (узлы 13/14 — `any`, годятся ли Яндексу по affinity-правилу — проверить в `proxy_pool.acquire`). - `SCRAPER_PROXY_URL` в `.env.runtime` указывает на выключенный узел — решение владельца: снять фолбэк или перевести на живой.
Author
Collaborator

Приёмка на проде (06.09 ~22:50 UTC, деплой 68759927): docker exec tradein-backend printenvUSE_PROXY_POOL_BROWSER=true, USE_PROXY_POOL_CURL=true (в старом контейнере переменных не было — маркер различает). Повтор пробы full-load (run 6157, coop-cancel после 2 закрытых бакетов, ~2.5 мин):

до (#6153, 22:00) после (#6157)
бакетов partial / done 18 / 0 0 / 2
unique_fetched 0 250
saved_inserted / saved_updated 0 / 0 39 / 200
price_history_rows 0 76
yandex-объявлений с last_seen_at в окне прогона 0 250
InvalidIP в логе сайдкара за 10 мин 83/3ч 0
сайдкар [yandex] … override=True 0 есть (relaunch с прокси из аренды)

П. (а) закрыт по эффекту. Открыты хвосты из комментария выше: (в) IMV /estimate без proxy_provider, _PoolCurlConfig, единственный yandex-узел, SCRAPER_PROXY_URL на выключенном узле.

**Приёмка на проде (06.09 ~22:50 UTC, деплой `68759927`):** `docker exec tradein-backend printenv` → `USE_PROXY_POOL_BROWSER=true`, `USE_PROXY_POOL_CURL=true` (в старом контейнере переменных не было — маркер различает). Повтор пробы full-load (run **6157**, coop-cancel после 2 закрытых бакетов, ~2.5 мин): | | до (#6153, 22:00) | после (#6157) | |---|---:|---:| | бакетов `partial` / `done` | 18 / 0 | 0 / 2 | | `unique_fetched` | 0 | **250** | | `saved_inserted` / `saved_updated` | 0 / 0 | 39 / 200 | | `price_history_rows` | 0 | 76 | | yandex-объявлений с `last_seen_at` в окне прогона | 0 | 250 | | `InvalidIP` в логе сайдкара за 10 мин | 83/3ч | **0** | | сайдкар `[yandex] … override=True` | 0 | есть (relaunch с прокси из аренды) | П. (а) закрыт по эффекту. Открыты хвосты из комментария выше: (в) IMV `/estimate` без `proxy_provider`, `_PoolCurlConfig`, единственный yandex-узел, `SCRAPER_PROXY_URL` на выключенном узле.
Author
Collaborator

Радиус мёртвого env-фолбэка по факту (Loki, tradein-browser, строки /fetch 503 — браузер недоступен (proxy may be down), глубина хранения от 04.09):

день cian avito yandex
04.09 1757 130 0
05.09 2867 110 76 (проба #3375)
06.09 60 0 0

Итого 5000 отказов за трое суток, из них cian 4684. Падение 06.09 — после #3382 (cian_history_backfill в пул, деплой ~01:50 UTC) и #3387 (флаги backend). Остаток cian 06.09 — фаза houses cian-свипа (#3394, 24 отказа в run 6179) + фоновый retry сайдкара. То есть «пул за флагом включён с #2126/#2160» на практике держался на env-фолбэке для большинства cian-путей, и смерть узла 9 (02.09) обнулила их молча — phase-honest-status писал «причина не установлена».

**Радиус мёртвого env-фолбэка по факту (Loki, `tradein-browser`, строки `/fetch 503 — браузер недоступен (proxy may be down)`, глубина хранения от 04.09):** | день | cian | avito | yandex | |---|---:|---:|---:| | 04.09 | 1757 | 130 | 0 | | 05.09 | 2867 | 110 | 76 (проба #3375) | | 06.09 | 60 | 0 | 0 | Итого 5000 отказов за трое суток, из них cian 4684. Падение 06.09 — после #3382 (cian_history_backfill в пул, деплой ~01:50 UTC) и #3387 (флаги backend). Остаток cian 06.09 — фаза `houses` cian-свипа (#3394, 24 отказа в run 6179) + фоновый retry сайдкара. То есть «пул за флагом включён с #2126/#2160» на практике держался на env-фолбэке для большинства cian-путей, и смерть узла 9 (02.09) обнулила их молча — phase-honest-status писал «причина не установлена».
Author
Collaborator

П. (в) — поправка и масштаб (06.09). IMV из /estimatecurl_cffi, не сайдкар (_get_or_fetch_imv_cached → evaluate_via_imv без browser_fetcher; 4 запроса одной сессией). Без proxy_provider curl_proxy_url шёл на SCRAPER_PROXY_URL = узел 9 (407) → Авито 403 → оценка без IMV-якоря. Логи backend за 7 дней: imv: cache MISS 02.09 ×2, 03.09 ×1, 04.09 ×2 — и ни одного fresh recommended после них (до 02.09 miss сопровождался результатом); оценок в эти дни 1–3/сутки, т.е. с 02.09 каждая оценка без IMV-якоря. PR #3397: proxy_provider=RealProxyProvider() в обоих вызовах + честный лог при пустом пуле (/estimate остаётся 200 без блока avito_imv).

**П. (в) — поправка и масштаб (06.09).** IMV из `/estimate` — **curl_cffi, не сайдкар** (`_get_or_fetch_imv_cached → evaluate_via_imv` без `browser_fetcher`; 4 запроса одной сессией). Без `proxy_provider` `curl_proxy_url` шёл на `SCRAPER_PROXY_URL` = узел 9 (407) → Авито 403 → оценка без IMV-якоря. Логи backend за 7 дней: `imv: cache MISS` 02.09 ×2, 03.09 ×1, 04.09 ×2 — и **ни одного `fresh recommended`** после них (до 02.09 miss сопровождался результатом); оценок в эти дни 1–3/сутки, т.е. с 02.09 каждая оценка без IMV-якоря. PR #3397: `proxy_provider=RealProxyProvider()` в обоих вызовах + честный лог при пустом пуле (`/estimate` остаётся 200 без блока `avito_imv`).
Author
Collaborator

Приёмка #3397 на проде (06.09 06:15 UTC, деплой 075cec4, USE_PROXY_POOL_CURL=true в backend). Проба POST /api/v1/trade-in/estimate изнутри контейнера с house_type/repair_state (без них IMV по условию вызова не идёт — первые две пробы это и показали), адрес без IMV-кэша:

imv: cache MISS key=6af8e846 — fetching fresh
proxy_pool: leased proxy id=14 provider=avito by=-1        ← проводка пула работает
IMV warm-up GET /evaluation/realty → HTTP 403
proxy_pool: mark_health id=14 ok=False … ; released proxy id=14
imv: auth/quota error — manual action required: Avito geocode-A: HTTP 403

Ответ 200 за 0.96 с, блок avito_imv в ответе есть — но это house-level якорь бэкфилла (imv_anchor, приоритет над imv_eval, estimator.py:3357), не живой IMV. Вывод: проводка пула (п. в) выполнена и проверена по значению; сам живой curl-путь IMV из /estimate отбивается Авито 403 на warm-up независимо от прокси (datacenter-защита, #562; браузерный флаг avito_imv_use_browser_fetcher читает только house_imv_backfill). Побочный эффект: каждый cache-miss с заполненными полями теперь пишет mark_health ok=False исправному узлу за отказ площадки — вынес в #3398.

**Приёмка #3397 на проде (06.09 06:15 UTC, деплой `075cec4`, `USE_PROXY_POOL_CURL=true` в backend).** Проба `POST /api/v1/trade-in/estimate` изнутри контейнера с `house_type`/`repair_state` (без них IMV по условию вызова не идёт — первые две пробы это и показали), адрес без IMV-кэша: ``` imv: cache MISS key=6af8e846 — fetching fresh proxy_pool: leased proxy id=14 provider=avito by=-1 ← проводка пула работает IMV warm-up GET /evaluation/realty → HTTP 403 proxy_pool: mark_health id=14 ok=False … ; released proxy id=14 imv: auth/quota error — manual action required: Avito geocode-A: HTTP 403 ``` Ответ 200 за 0.96 с, блок `avito_imv` в ответе есть — но это house-level якорь бэкфилла (`imv_anchor`, приоритет над `imv_eval`, `estimator.py:3357`), не живой IMV. **Вывод:** проводка пула (п. в) выполнена и проверена по значению; сам живой curl-путь IMV из `/estimate` отбивается Авито 403 на warm-up независимо от прокси (datacenter-защита, #562; браузерный флаг `avito_imv_use_browser_fetcher` читает только `house_imv_backfill`). Побочный эффект: каждый cache-miss с заполненными полями теперь пишет `mark_health ok=False` исправному узлу за отказ площадки — вынес в #3398.
Author
Collaborator

П. (в) закрыт целиком: PR #3397 (IMV, проводка пула) + PR #3399 (yandex/cian valuation) задеплоены; проба 06.09 07:15 UTC — cian sale=9.9M (acc=93 %) через узел 13, yandex items=20 через узел 1, 407 = 0. Детали — #3398.

П. (в) закрыт целиком: PR #3397 (IMV, проводка пула) + PR #3399 (yandex/cian valuation) задеплоены; проба 06.09 07:15 UTC — cian `sale=9.9M (acc=93 %)` через узел 13, yandex `items=20` через узел 1, 407 = 0. Детали — #3398.
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#3386
No description provided.