Skip to content

fix(cli): release disconnected event streams#11646

Merged
marius-kilocode merged 4 commits into
mainfrom
legend-bonnet
Jun 29, 2026
Merged

fix(cli): release disconnected event streams#11646
marius-kilocode merged 4 commits into
mainfrom
legend-bonnet

Conversation

@marius-kilocode

@marius-kilocode marius-kilocode commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Global SSE streams can survive client disconnects and reconnects under the current Effect Node HTTP stack. Each stale stream remains subscribed to GlobalBus with an unbounded callback queue. Because session.diff events can include complete multi-megabyte patches, repeated reconnects can retain tens of gigabytes of diff payloads.

Live heap snapshots from the leaking process showed the retained memory was dominated by duplicated Git patch strings, not native heap growth or database handles. At 31.1 GB physical footprint, the JavaScript heap was 15.78 GB, with 15.21 GB held by strings. The largest retained groups were repeated diff --git payloads from generated .windows-spike/zig-pkg/... files: the top Git patch string groups alone accounted for 10.89 GB. A previous 24 GB capture showed the same retainer path, then the later capture grew by another 4.07 GB of string storage as additional stale global event listeners and queued diff events accumulated.

The retained path was:

FileDiff.patch
  -> session.diff properties
  -> GlobalBusEvent.payload
  -> Effect callback queue messages
  -> Stream.callback listener closure
  -> GlobalBus._events.event
  -> GC root

The later heap contained 27 live GlobalBus listener closures for global SSE streams, while logs showed 29 global event connections and only 2 disconnections. Each orphaned listener held an unbounded queue. One representative queue contained more than 3,100 undelivered global events.

Observe the underlying Web request abort signal or Node request/socket close event and explicitly interrupt the SSE stream. Closing the stream scope runs its acquireRelease finalizer, unregisters the GlobalBus listener, and releases the queue immediately instead of depending on transport cancellation propagation. This removes the identified retention source: stale global SSE queues can no longer keep full diff payloads alive after clients disconnect.

Add a real Node HTTP server regression that repeatedly connects and disconnects global event clients and requires the listener count to return to its baseline after every disconnect. This restores the dead-stream cleanup guarantee previously addressed by #8952 for the current Effect-based route implementation.

@kilo-code-bot

kilo-code-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (4 files)
  • .changeset/release-disconnected-streams.md
  • packages/opencode/src/kilocode/server/sse.ts
  • packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts
  • packages/opencode/test/kilocode/server/httpapi-global-sse.test.ts

Reviewed by gpt-5.4-20260305 · Input: 119.4K · Output: 10.1K · Cached: 408.5K

Review guidance: REVIEW.md from base branch main

@marius-kilocode marius-kilocode merged commit 727ded7 into main Jun 29, 2026
26 checks passed
@marius-kilocode marius-kilocode deleted the legend-bonnet branch June 29, 2026 11:33
vkeerthivikram pushed a commit to vkeerthivikram/kilocode that referenced this pull request Jun 30, 2026
fix(cli): release disconnected event streams
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants