BrowserFetcher acquire/release-ил lease на КАЖДЫЙ /fetch. При росте пула с 1 до
3 узлов (id 9/10/11, affinity=any) acquire() стал гарантированно выдавать
ДРУГОЙ прокси на каждый запрос (ORDER BY last_ok_at NULLS LAST) — tradein-browser
релончит camoufox при любой смене желаемого прокси, теряя сессионные куки и
усиливая антибот-подозрительность (поток "первых визитов").
Теперь lease берётся один раз в __aenter__ на весь жизненный цикл BrowserFetcher
(целый прогон, часы) и отпускается один раз в __aexit__ (finally — не течёт при
исключении). Коллизия с reap_stale_leases (30 минут, а прогоны бывают часами):
не увеличивали порог (ослабило бы crash-recovery мёртвых run'ов), вместо этого
proxy_pool.touch() — heartbeat, продлевающий leased_at на каждый /fetch, пока
сессия жива; без heartbeat'а (упавший run) lease реапится как раньше. mark_health
остаётся на каждый /fetch (не огрублена до раза в сессию) + осознанная ротация
lease после 3 подряд неудач (не мечемся, как раньше).
Не трогали: acquire()-сортировку, curl-путь (providers/_proxy.py — сессии там и
так короткоживущие, per-request пересоздание не несёт cost релонча браузера),
run_id (только log-attribution, не требуется для фикса).