From adc5d447aefc39ef33915dfc108d80f512e15d08 Mon Sep 17 00:00:00 2001 From: Shi Dong Date: Wed, 22 Jul 2026 07:40:02 -0700 Subject: [PATCH] tbench2 env server: enable idle-session reaper to reclaim leaked slots The /mcp WebSocket handler releases its session slot and Docker container only in a finally block, which runs solely on a clean socket close. Half-open or uncleanly-dropped connections never raise WebSocketDisconnect, so receive_text() blocks forever, finally never runs, and the slot + container leak. Over ~20-24h these accumulate until all MAX_CONCURRENT_ENVS slots are held and new episodes deadlock on CAPACITY_REACHED, requiring a manual env-server reset. create_app was called with a bare max_concurrent_envs, which the core server maps to ConcurrencyConfig(session_timeout=None) -> the idle-session reaper is disabled. Pass an explicit ConcurrencyConfig with session_timeout instead (SESSION_IDLE_TIMEOUT_S env var, default 3600s) so _reap_idle_sessions runs and evicts stale sessions, reclaiming their containers automatically. --- envs/tbench2_env/server/app.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/envs/tbench2_env/server/app.py b/envs/tbench2_env/server/app.py index 67486680b..d5e6f32f5 100644 --- a/envs/tbench2_env/server/app.py +++ b/envs/tbench2_env/server/app.py @@ -30,12 +30,16 @@ Environment Variables: TB2_MODE: Execution mode - "local" (default), "docker", or "auto" MAX_CONCURRENT_ENVS: Maximum concurrent WebSocket sessions (default: 8) + SESSION_IDLE_TIMEOUT_S: Idle seconds before a session is reaped and its + container reclaimed (default: 3600). Guards against leaked slots from + half-open WebSocket connections that never fire a clean disconnect. """ import os try: + from openenv.core.env_server import ConcurrencyConfig from openenv.core.env_server.http_server import create_app # In-repo imports @@ -46,6 +50,7 @@ from models import Tbench2Action, Tbench2Observation # Standalone imports (when environment is standalone with openenv from pip) + from openenv.core.env_server import ConcurrencyConfig from openenv.core.env_server.http_server import create_app from server.tbench2_env_environment import ( Tbench2DockerEnvironment, @@ -72,13 +77,17 @@ # Create the app with web interface and README integration max_concurrent = int(os.getenv("MAX_CONCURRENT_ENVS", "8")) +session_idle_timeout = float(os.getenv("SESSION_IDLE_TIMEOUT_S", "3600")) app = create_app( _DEFAULT_ENVIRONMENT, Tbench2Action, Tbench2Observation, env_name="tbench2_env" + _ENV_SUFFIX, - max_concurrent_envs=max_concurrent, + concurrency_config=ConcurrencyConfig( + max_concurrent_envs=max_concurrent, + session_timeout=session_idle_timeout, + ), )