Skip to content

(7/7) feat(session): launch the supervisor topology; delete the single-process chassis#1519

Open
guapisolo wants to merge 11 commits into
refactor/session-mp-test-harnessfrom
refactor/session-mp-control-plane
Open

(7/7) feat(session): launch the supervisor topology; delete the single-process chassis#1519
guapisolo wants to merge 11 commits into
refactor/session-mp-test-harnessfrom
refactor/session-mp-control-plane

Conversation

@guapisolo

@guapisolo guapisolo commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

The switchover: start_session_server now always builds and starts a SessionServerSupervisor — one topology for every worker count, so the if workers > 1 launch branch never exists — and the single-process chassis is deleted in the same move.

Stacked on #1569#1510#1563#1518#1565#1566. Base = refactor/session-mp-test-harness. This PR was split for reviewability: the process-lifecycle layer is #1565, the test-harness migration (the equivalence evidence for the deletion below) is #1566. Merge order: #1565#1566 → this.

What's here

  • router_manager.start_session_server — always supervisor; returns it. rollout_manager holds it and calls check() at the start of both generate() and eval(), so a dead worker fails the rollout loudly instead of black-holing its hash shard.
  • arguments.py--session-server-workers (int, default 8: idle workers cost ~zero CPU, and under concurrent multi-turn load GIL-bound tokenization/deserialization scales with cores; memory scales with N — each worker loads its own tokenizer).
  • sessions.py + server.py deleted. Two chassis duplicate the route surface (the same six routes defined twice) and the proxy stack (SessionServer.do_proxy vs the worker's ProxyBackend, held identical only by equivalence tests), and every future behavior change pays that tax twice. The old workers=1 server was already a spawned child; the delta is one extra tokenizer-free router child and a per-request IPC hop measured in µs against LLM-turn latencies.
  • Dev doc + header wording updated to the one-chassis reality (docs/developer/multi-process-session-server.md, core.py, worker.py, processing_utils.py).

Testing

Not here

Round-robin / no-HOL IPC stays cut (benchmark-gated, see the dev doc). The R3 client-strip landed below this stack in #1563. The worker-direct records path (decided, see the dev doc) and the benchmark port are follow-up PRs.

🤖 Generated with Claude Code

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an opt-in multi-process session server supervisor (SessionServerSupervisor) that spawns and monitors multiple worker processes and a router process to shard sessions, controlled by the new --session-server-workers argument. The review feedback highlights several critical reliability and resource management improvements: ensuring the supervisor is explicitly shut down in RolloutManager.dispose() to prevent port-binding conflicts, handling process spawning failures gracefully to avoid orphan processes, unregistering from atexit to prevent memory leaks, and making the supervisor's shutdown method thread-safe using a lock.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread miles/ray/rollout/rollout_manager.py
Comment thread miles/rollout/session/session_supervisor.py Outdated
Comment thread miles/rollout/session/session_supervisor.py
Comment thread miles/rollout/session/session_supervisor.py
Comment thread miles/rollout/session/session_supervisor.py Outdated
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 2e99b9d to 31c6638 Compare June 30, 2026 21:48
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 9e9dd8c to a1b8548 Compare June 30, 2026 21:48
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 31c6638 to 506e3da Compare June 30, 2026 22:57
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from a1b8548 to 1c688b8 Compare June 30, 2026 22:57
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 506e3da to e4dfc23 Compare June 30, 2026 23:14
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 1c688b8 to 9e0fc91 Compare June 30, 2026 23:14
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from e4dfc23 to 2e7fdc6 Compare July 1, 2026 19:43
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 9e0fc91 to 18e6d8b Compare July 1, 2026 19:43
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 2e7fdc6 to 756bdbb Compare July 1, 2026 20:15
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 18e6d8b to 17c5f1d Compare July 1, 2026 20:15
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 756bdbb to 2076ac7 Compare July 1, 2026 23:37
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 17c5f1d to 1bb4956 Compare July 1, 2026 23:37
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 2076ac7 to 2e76cb4 Compare July 2, 2026 21:22
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 1bb4956 to fceaa54 Compare July 2, 2026 21:29
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 2e76cb4 to 9668f79 Compare July 2, 2026 22:31
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from fceaa54 to e05b81a Compare July 2, 2026 22:31
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 9668f79 to 948ef7a Compare July 2, 2026 22:38
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from e05b81a to dcedb28 Compare July 2, 2026 22:38
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 948ef7a to 4ccc465 Compare July 2, 2026 22:48
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch 2 times, most recently from 165ffc1 to 3c1d6e0 Compare July 2, 2026 23:17
@guapisolo guapisolo changed the title feat(session): multi-process control plane — supervisor + --session-server-workers wiring feat(session): control plane — the supervisor becomes the one session-server chassis Jul 2, 2026
Comment thread miles/rollout/session/session_ipc.py
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from 4ccc465 to b7b352c Compare July 2, 2026 23:58
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 3c1d6e0 to 45f0e76 Compare July 2, 2026 23:58
@guapisolo guapisolo force-pushed the refactor/session-mp-dataplane branch from b7b352c to 3421e64 Compare July 3, 2026 00:04
@guapisolo guapisolo changed the title feat(session): launch the supervisor topology; delete the single-process chassis (7/7) feat(session): launch the supervisor topology; delete the single-process chassis Jul 3, 2026
@guapisolo guapisolo force-pushed the refactor/session-mp-test-harness branch from bc6f752 to 736dd29 Compare July 6, 2026 05:14
@guapisolo guapisolo marked this pull request as ready for review July 6, 2026 05:38
@guapisolo guapisolo force-pushed the refactor/session-mp-test-harness branch from 736dd29 to 2fac59c Compare July 6, 2026 05:57
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from 69b53e8 to 8d6973a Compare July 6, 2026 05:57
@guapisolo

Copy link
Copy Markdown
Collaborator Author

@guapisolo guapisolo force-pushed the refactor/session-mp-test-harness branch from 2fac59c to e87eab4 Compare July 6, 2026 09:08
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from a7bfd2e to 4805a46 Compare July 6, 2026 09:40
guapisolo added a commit that referenced this pull request Jul 6, 2026
…ose()

dispose() released the metric checker and health monitors but left the
supervisor's worker/router processes and their bound port to the atexit
hook, which only fires when the actor process exits — an actor disposed
and reused would still hold the port. shutdown() is idempotent and
thread-safe, so the atexit backstop stays harmless.

Addresses the Gemini review comment on #1519.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@guapisolo guapisolo force-pushed the refactor/session-mp-test-harness branch from ee3eeeb to e755326 Compare July 6, 2026 10:06
guapisolo and others added 11 commits July 6, 2026 10:07
…ess chassis

start_session_server now always builds and starts a
SessionServerSupervisor — one topology for every worker count;
--session-server-workers=1 (the new flag's default) is simply N=1, not a
separate code path. It returns the supervisor, and rollout_manager calls
check() at the start of BOTH generate() and eval(), so a dead worker
fails the rollout loudly instead of letting requests to its hash shard
hang.

sessions.py and server.py are deleted. Two chassis duplicate the
route surface (the same six routes defined twice) and the proxy stack
(SessionServer.do_proxy vs the worker's ProxyBackend, held identical
only by equivalence tests), and every future behavior change pays that
tax twice. The old workers=1 server was already a spawned child; the
delta is one extra tokenizer-free router child and a per-request IPC hop
measured in µs against LLM-turn latencies.

Doc and header wording that referenced the deleted chassis is updated to
the one-chassis reality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rationale (fail-fast on a dead worker's hash shard, called from both
generate() and eval()) lives in the doc-dev spec and supervisor.check()'s
docstring; the inline restatement was redundant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ling knob

The old help text described the pre-switchover behavior ("1 = single-process,
no router/IPC"), contradicting the one-topology reality. Both spots now say
when to turn the knob: if the session server is the bottleneck, more workers =
more interpreters, so GIL-bound tokenization/deserialization scales with cores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Goal and the requirements bullet now just say what runs (supervisor, N
workers, one thin router) instead of arguing against a second chassis;
the rationale stays in the PR/commit history. The workers=1-topology
parity bullet goes too, restoring agreement with the design-targets line
that references two known deltas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Idle workers cost ~zero CPU (a sleeping event loop + IPC reader), and
under concurrent multi-turn load a single worker's GIL serializes
tokenization/deserialization, so a multi-core default speeds up typical
agentic runs out of the box. Cost is memory (each worker is its own
interpreter + tokenizer, ~0.3-0.5 GB) and 9 child processes instead of 2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rence

The whole group (--use-session-server through --tito-allowed-append-roles)
was never in the complete reference; --session-server-workers landing here
makes the gap visible, so the section arrives with all six flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ntials

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deleted chassis was imported at router_manager's module top too, so
this restores the pre-switchover import weight — no reason to defer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rgon

Also clears two leftovers the switchover missed: create_session's inline
comment still described the deleted single-process path, and the doc's
decomposition bullet still said 'reused unchanged by both chassis'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ose()

dispose() released the metric checker and health monitors but left the
supervisor's worker/router processes and their bound port to the atexit
hook, which only fires when the actor process exits — an actor disposed
and reused would still hold the port. shutdown() is idempotent and
thread-safe, so the atexit backstop stays harmless.

Addresses the Gemini review comment on #1519.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same comment-only sweep, applied to the docstrings this branch
introduces or rewrites (core, worker, router_manager).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@guapisolo guapisolo force-pushed the refactor/session-mp-control-plane branch from bed5328 to d65f45b Compare July 6, 2026 10:09

@Shi-Dong Shi-Dong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Great job.
But let's postpone merging the chain now, as it's intrusive and might break what is currently working on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants