netx/netx_api/webcrt_io.py
oliver f64835749a Expose worker scheduler health via heartbeat and isolate WebCRT IO.
API /metrics and /health/ready now read a worker heartbeat when collectors are split out; WebCRT blocking I/O uses a dedicated executor so session pumps do not starve the default pool.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 18:37:06 +08:00

42 lines
1.4 KiB
Python

"""Dedicated thread pool for WebCRT blocking I/O (stdout take / stdin / resize).
Keeps session pumps off the default asyncio executor so HTTP handlers and other
``run_in_executor(None, ...)`` callers are not starved under many CRT sessions.
"""
from __future__ import annotations
import logging
import threading
from concurrent.futures import ThreadPoolExecutor
from .config import settings
_log = logging.getLogger("netx.webcrt.io")
_lock = threading.Lock()
_executor: ThreadPoolExecutor | None = None
def webcrt_io_executor() -> ThreadPoolExecutor:
global _executor
with _lock:
if _executor is None:
# Cap workers: each attached WS holds one blocking take_stdout wait.
n = max(4, min(48, int(getattr(settings, "webcrt_max_sessions", 40) or 40)))
_executor = ThreadPoolExecutor(max_workers=n, thread_name_prefix="webcrt-io")
_log.info("webcrt io executor started workers=%s", n)
return _executor
def shutdown_webcrt_io_executor(*, wait: bool = False) -> None:
global _executor
with _lock:
if _executor is None:
return
try:
_executor.shutdown(wait=wait, cancel_futures=True)
except TypeError:
_executor.shutdown(wait=wait)
except Exception: # noqa: BLE001
_log.exception("webcrt io executor shutdown failed")
_executor = None