fix(pty-daemon): pause flooding PTYs on backpressure instead of destroying the shared socket
Merged
KI
4mo ago+195+195+012345678901234567890123456789-1-1-0123456789
Links
- Findings + design context:
plans/20260717-host-service-git-worker-pool.md(#5746)
Summary
- One full-rate flooding terminal (e.g.
yes, a huge build log) used to trip the daemon's 8 MiB outbound cap andsocket.destroy()the daemon↔host-service connection — severing every terminal in the org (host-service logspty-daemon disconnected; closing N terminal WS socket(s); users see all terminals blip). Reproduced deterministically in the 2026-07-17 stress test. - Fix: per-session flow control. When a subscriber's socket buffers past 1 MiB, pause the producing PTYs (
pty.pause()); resume on socketdrain. The kernel PTY buffer fills and the flooding process blocks on its own write — it throttles itself at the source. The 8 MiB destroy stays as a last-resort for pathological control-message floods.
How It Works
Ptygainspause()/resume(): node-pty's public pause/resume (added upstream for VS Code's ptyHost flow control, same actuator) for spawned sessions,ReadStream.pause()for adopted-fd sessions.Server.wireSession: after fanning out a chunk, any subscribed conn withwritableLength > outboundPauseThresholdrecords the session inconn.pausedSessionsand the PTY is paused. Paused PTYs emit no further data, so this fires once per congestion episode.- Resume paths: socket
drain(guaranteed to fire — the buffer only passes the threshold afterwrite()returned false), and conn drop (a destroyed socket never drains). A session held paused by another still-congested conn stays paused. - This is not a return of the removed ACK flow control (SUPER-939): no client involvement, no protocol change — the congestion signal is the daemon's own socket buffer. Renderer-slow congestion is still handled by host-service's existing bounded buffering; this covers the daemon→host-service hop.
Manual QA Checklist
- Flood a terminal (
yes) with other terminals open — no org-wide disconnect, other terminals stay live (covered by integration test with real shells + real unix socket; app-level re-run of the CDP flood stress is a follow-up)
Testing
- New
test/flow-control.test.ts(node --test, real/bin/sh+yesflood + real unix socket):- slow consumer → connection survives, output resumes after the consumer drains
- consumer hard-disconnect while paused → next subscriber still gets output (conn-drop resume path)
- Both proven to fail against the old behavior (mutation-tested: destroy-only → test 1 fails; no conn-drop resume → test 2 fails)
- Full
bun run test:integration(51/51) andbun testunit suite (57/57) pass bun run typecheck(pty-daemon + host-service),bun run lintclean
Risks / Rollout
- Risk: a paused PTY blocks the foreground process's writes — that's the intended semantics (same as VS Code), and pause windows are bounded by drain/disconnect. Sessions with no subscriber are never paused, so background jobs in unattached terminals are unaffected.
- Rollback: revert; behavior returns to destroy-on-overflow.
Summary by cubic
Prevent org-wide terminal disconnects by pausing flooding PTYs on backpressure instead of destroying the shared daemon socket. Adds per-session flow control to pty-daemon so heavy output (e.g., yes/build logs) doesn’t take down other terminals.
- Bug Fixes
- Implemented per-connection flow control: when a subscriber’s
socket.writableLengthexceeds a threshold (default 1 MiB viaoutboundPauseThreshold), pause the session’s PTY; resume on socketdrainor connection drop. The 8 MiBoutboundBufferCapremains a last-resort. - Extended
Ptywithpause()/resume()and wired bothnode-ptysessions and adopted-fd sessions. - Tracked
pausedSessionsper connection to avoid resuming while another subscriber is still congested. - Added
test/flow-control.test.tsand included it intest:integrationto cover slow consumer and disconnect-resume paths.
- Implemented per-connection flow control: when a subscriber’s
Summary by CodeRabbit
-
New Features
- Added PTY flow-control support to pause output when clients cannot keep up and resume it when capacity is available.
- Added configurable outbound pause thresholds for server connections.
- Improved handling of disconnected clients so shared sessions remain available and recover correctly.
-
Tests
- Added integration coverage for slow, interrupted, and reconnecting consumers during high-volume PTY output.