fix(tradein/proxy): узел, зарезервированный за Домкликом, до Домклика не доходит (#2800 A) (#2802)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m6s
Deploy Trade-In / build-backend (push) Successful in 1m3s
Deploy Trade-In / deploy (push) Successful in 6m36s

This commit is contained in:
bot-backend 2026-08-09 18:15:55 +00:00
parent f45ce3be2d
commit 7cd8c63b89
2 changed files with 67 additions and 8 deletions

View file

@ -228,14 +228,21 @@ def acquire(db: Session, provider: str, *, run_id: int | None = None) -> ProxyLe
чужая только запасной вариант, чтобы источник не голодал при живых свободных узлах чужая только запасной вариант, чтобы источник не голодал при живых свободных узлах
чужой affinity (#2600). чужой affinity (#2600).
Fallback НЕ трогает последний enabled-узел выделенной (не-'any') affinity см. Fallback НЕ трогает последний enabled-узел выделенной (не-'any') affinity: если
173_scrape_proxies_add_domclick_affinity.sql: у domclick ровно один узел (id=1), fallback заберёт его под чужой источник, «свой» останется без прокси вообще хуже,
намеренно вырезанный из общего пула, потому что QRATOR банит все прокси кроме этого чем голодание исходного источника, которое фикс призван устранить. Кандидат
одного чистого residential-адреса. Если fallback заберёт его под avito/cian/yandex, участвует в fallback, только если его affinity='any' ИЛИ у этой affinity есть ДРУГОЙ
domclick останется без прокси вообще хуже, чем голодание исходного источника, enabled-узел (EXISTS-подзапрос) т.е. выдача не обнулит доступность выделенной
которое фикс призван устранить. Кандидат участвует в fallback, только если его affinity целиком.
affinity='any' ИЛИ у этой affinity есть ДРУГОЙ enabled-узел (EXISTS-подзапрос)
т.е. выдача не обнулит доступность выделенной affinity целиком. Исторический повод для этой защиты (173_scrape_proxies_add_domclick_affinity.sql
единственный residential-узел id=1, закреплённый за domclick, потому что QRATOR
банил остальные) снят миграцией 253 (#2800): живая проба показала, что как раз до
рабочего хоста Домклика (bff-search-web.domclick.ru) этот узел НЕ доходит, а
Авито/Яндекс через него работают резервация держала узел за источником, которому
он не годен, и прятала от тех, кому годен. Узлов с выделенной affinity на проде
сейчас нет, но САМА защита остаётся: значение 'domclick' допустимо констрейнтом, и
следующий выделенный узел должен получить её сразу, а не после повторного разбора.
ОБА запроса отсекают узлы с АКТИВНЫМ баном по ЭТОМУ provider'у ОБА запроса отсекают узлы с АКТИВНЫМ баном по ЭТОМУ provider'у
(scrape_proxy_source_bans.banned_until > now(), #2600 п.2) — узел, забаненный Авито, (scrape_proxy_source_bans.banned_until > now(), #2600 п.2) — узел, забаненный Авито,

View file

@ -0,0 +1,52 @@
-- 253_scrape_proxy_domclick_affinity_release.sql
-- Снять с узла резервацию provider_affinity='domclick' (#2800).
--
-- WHY (замер, не рассуждение — живая проба 09.08.2026, тракт сайдкар+camoufox,
-- POST /fetch на robots.txt рабочего хоста каждой площадки):
--
-- узел | affinity | avito | ekb.cian.ru | realty.yandex.ru | bff-search-web.domclick.ru
-- -----+----------+-------+--------------------+------------------+---------------------------
-- 1 | domclick | 200 | 200 «Ошибка — Циан»| 200 | 500 NS_ERROR_PROXY_BAD_GATEWAY
-- 9 | any | 200 | 200 | 200 | 200
-- 10 | any | 200 | 200 | 200 | 200
-- 11 | any | 200 | 200 | 200 | 200
--
-- Узел, закреплённый 173-й миграцией СПЕЦИАЛЬНО за Домкликом, до рабочего хоста
-- Домклика не доходит вообще (NS_ERROR_PROXY_BAD_GATEWAY на bff-search-web —
-- именно туда ходит боевой сбор, см. providers/domclick/serp.py::_BFF_BASE), при
-- этом Авито и Яндекс через него отвечают штатно. Резервация даёт ровно обратный
-- эффект задуманному: единственный источник, которому узел ГОДЕН НЕ БЫЛ, держал его
-- за собой, а два источника, которым он годен, его не видели —
-- acquire('avito'|'yandex') отбирает по provider_affinity IN (source,'any'), а
-- fallback этот узел не берёт (защита последнего узла выделенной affinity).
--
-- 'any', а НЕ enabled=false: узел жив для двух площадок из четырёх, выключать его
-- целиком — терять четверть и без того дефицитного пула (#2638).
--
-- WHAT:
-- provider_affinity='domclick' → 'any' для узлов, у которых affinity именно такая.
-- CHECK-констрейнт (173) не трогаем: значение 'domclick' остаётся допустимым, если
-- в пуле появится узел, который до Домклика реально доходит.
--
-- ЧТО ЭТА МИГРАЦИЯ НЕ ДЕЛАЕТ (граница честная):
-- Она НЕ чинит Домклик. acquire('domclick') и до неё видел все четыре узла
-- (affinity IN ('domclick','any')), т.е. шанс вытянуть узел 1 и потратить первый
-- бакет впустую был и остаётся 1/4 — закрывает это проба по паре «узел × источник»
-- (#2800 часть B), а не смена affinity. Здесь снимается только резервация.
--
-- IDEMPOTENCY / SAFETY:
-- Один UPDATE в транзакции; повторный прогон не находит строк (no-op) — auto-apply
-- strict на деплое это требует. Блокирующего DDL нет (см.
-- scripts/check-migration-lock-timeout.py: правило про ALTER/DROP/CREATE INDEX),
-- UPDATE берёт row-lock на единичные строки.
--
-- Dependencies: 157_scrape_proxies.sql, 173_scrape_proxies_add_domclick_affinity.sql
BEGIN;
UPDATE scrape_proxies
SET provider_affinity = 'any',
updated_at = now()
WHERE provider_affinity = 'domclick';
COMMIT;