[coding_agent_rl] middleware: shutdown_session drains in-flight handl… - #1954
Merged
zhuzilin merged 2 commits intoMay 27, 2026
Merged
Conversation
…in-flight handlers before sglang release_memory_occupation When a SWE rollout sample returns, slime's train loop will later call release_memory_occupation, which hard-asserts sglang's `is_fully_idle()` and SIGQUITs the scheduler on miss. Straggler claude-cli requests from the just-torn-down sandbox can still reach sglang via middleware's `setdefault(sid, Session())` at that moment, tripping the assert. Every multi-step SWE RL run was crashing on the first offload after a rollout step. Two-layer close in the middleware (only examples/ touched, slime backend untouched): - module-level `_inflight: dict[sid, set[Task]]` tracks every running `_handle_request`; lives outside the Session dataclass so it survives `pop_session_split` - module-level `_closed: set[sid]` tombstones drained sids; `_handle_request` early-returns 503 for them so stragglers cannot silently `setdefault` a fresh Session and reach sglang - `shutdown_session(sid)`: `_closed.add(sid)`, then `asyncio.wait` the in-flight bucket, cancel + gather pending (cancel fires `/abort_request` to sglang via `_generate`'s cleanup) - `pop_session_split` deliberately does NOT discard from `_closed` (permanent tombstone). sids are unique per rollout step so the set only grows linearly per process - `generate.py` finally-block calls `shutdown_session` before `pop_session_split`; idempotent on the happy path Does NOT wait for sglang idle here -- slime's `sglang_engine.release_memory_occupation` already calls `flush_cache()` with 60x1s polling (returns 200 only when scheduler is idle), strictly stronger than anything we could probe from the middleware. Once `_closed` is set and local handlers are drained, no new request can reach sglang, so the backend's existing flush_cache covers the actual idle wait. Validated 2026-05-26 on 8-node Qwen3.6-35B-A3B SWE RL: step 0 (rollout + train + weight update + offload) and step 1 (rollout + train, loss -1.03, grad_norm 3.36) both completed cleanly.
jingshenghang
force-pushed
the
coding-agent-rl-shutdown-session
branch
from
May 27, 2026 04:00
22fa81f to
9c098ca
Compare
zhuzilin
approved these changes
May 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ers before sglang release_memory_occupation
When a SWE rollout sample returns, slime's train loop immediately calls release_memory_occupation, which hard-asserts sglang's
is_fully_idle(). Straggler claude-cli requests from the just-torn-down sandbox can still be in the sglang pipeline (running_batch / chunked_req / result_queue / hicache writes) at that exact moment, tripping the assert and SIGQUITing the scheduler subprocess. Every multi-step SWE RL run was crashing on the first offload after a rollout step.Three-layer drain in the middleware (only examples/ touched, slime backend untouched):
_inflight: dict[sid, set[Task]]tracks every running_handle_request; lives outside the Session dataclass so it survivespop_session_split_closed: set[sid]tombstones drained sids;_handle_requestearly-returns 503 for them so stragglers cannot silentlysetdefaulta fresh Session and reach sglangshutdown_session(store, sid):_closed.add(sid)asyncio.waitthe in-flight bucket, cancel + gather pendingsettle_sec=3.0(sglang/abort_requestis fire-and-forget IPC; takes several scheduler ticks to actually clear the batch)GET sglang_url/flush_cache?timeout=N-- sglang's own native idle-wait via_check_pending_flush, strictly stronger than the settle sleeppop_session_splitdeliberately does NOT discard from_closed(permanent tombstone). sids are unique per rollout step so the set only grows linearly per process.generate.pyfinally-block callsshutdown_sessionbeforepop_session_split; idempotent on the happy path.Validated 2026-05-26 on 8-node Qwen3.6-35B-A3B SWE RL: step 0 (rollout_0