build_browser_fetcher получил keyword-only reuse_context (дефолт False — остальные
шесть вызывающих не меняются), и свип домклика зовёт фабрику с reuse_context=True.
Почему. Свип был единственным, кто остался на холодном пути: сайдкар на каждый
/fetch делал browser.new_page() и поднимал новый камуфокс. Прод-замер 17.09,
прогон 7389 (Москва): 600 страниц за 3ч05м, 1 корзина из 6, память сайдкара
426 МиБ → 1.79 ГиБ, в логе «запуск AsyncCamoufox» между почти каждым запросом.
Якорная вкладка (#3118) в таком режиме не переживает фетч, её поднимают заново
через goto(origin), и именно эти goto валятся таймаутом 60с, убивая корзину
целиком — так подряд легли '5+', 'st', '1'. Оба detail-backfill'а давно ходят
с reuse_context=True; там же записан замер: 26 холодных фетчей = 100% блок, те же
карточки в тёплом контексте = 5/5 примерно по 2с.
Упавшая корзина сбрасывает контекст. Тёплый контекст, сгоревший на одной корзине,
иначе переползает в следующую — ровно этот каскад и наблюдался. Сброс стоит в
generic except перед continue, где следующий fetch() его и доставит.
В ветке DomClickBlockedError сброса СОЗНАТЕЛЬНО нет: request_context_reset() лишь
взводит _context_reset_pending, а тот уезжает в сайдкар ключом reset_context
следующего fetch(), которого после break не будет. Остаётся известная дыра —
_contexts[provider] в сайдкаре живёт без TTL, так что сожжённый блоком контекст
достаётся следующему прогону; закрыть её можно только отдельной ручкой сброса
в сайдкаре, это отдельная задача.
Три двойника фетчера в тестах дополнены request_context_reset / reuse_context —
без этого они падали TypeError/AttributeError, то есть проверяли собственную
неполноту вместо поведения свипа.
Сьют: 6473 passed, 74 skipped.