All checks were successful
CI Trade-In / changes (pull_request) Successful in 19s
CI / changes (pull_request) Successful in 40s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 2m0s
CI Trade-In / backend-tests (pull_request) Successful in 8m5s
Инстанс camoufox поднимался на поставщика и не закрывался никогда: пять поставщиков давали пять вечных браузеров в контейнере с mem_limit 2560m. Прод 15.09-17.09: 5 живых простояли 2.4 ч прямо перед резким рестартом 16.09 19:08; медиана working_set 1494 МиБ при 3 живых, 1956 при 4, 2108 при 5. Потолок BROWSER_MAX_INSTANCES (по умолчанию 3) проверяется в _launch_browser, через который идут все запуски, включая фоновый retry. На потолке закрывается простаивающий инстанс другого поставщика: без context'а первым, по давности использования внутри класса. Занятый лок не трогается, если закрыть некого, запуск идёт сверх потолка с предупреждением. Слот резервируется на время запуска. _close_browser снимает инстанс со словарей до первого await и больше не сбрасывает пейсинг: это делают только собственный relaunch и смена прокси, как раньше. Четыре дефекта ревью ветки fix/3412-cian-instance-relaunch закрыты тестами, каждый краснеет при снятии своей части фикса. init: true у сервиса browser: PID 1 был python server.py и не собирал осиротевших детей закрытого браузера; прод 17.09 — 2296 процессов в Z за 14 ч. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
24 lines
1.1 KiB
Python
24 lines
1.1 KiB
Python
"""#3412: у tradein-browser PID 1 — init, который собирает осиротевших детей camoufox.
|
||
|
||
Без `init: true` PID 1 контейнера — `python server.py`; дочерние процессы закрытого
|
||
браузера переходят к нему и остаются зомби (прод 17.09.2026: 2296 процессов в Z за 14 ч).
|
||
Проверка по значению: разобранный compose, сервис с образом tradein-browser.
|
||
"""
|
||
|
||
from pathlib import Path
|
||
|
||
import yaml
|
||
|
||
_COMPOSE = Path(__file__).resolve().parents[2] / "docker-compose.prod.yml"
|
||
|
||
|
||
def test_browser_service_runs_under_init() -> None:
|
||
services = yaml.safe_load(_COMPOSE.read_text())["services"]
|
||
browser = {
|
||
name: svc
|
||
for name, svc in services.items()
|
||
if "gendesign-tradein-browser" in svc.get("image", "")
|
||
}
|
||
# Контроль разбора: пустой словарь дал бы зелёный тест ни о чём.
|
||
assert set(browser) == {"browser"}, sorted(browser)
|
||
assert browser["browser"].get("init") is True
|