Skip to content

bug(sdk): StreamController buffers every event forever for callback consumers — unbounded memory growth #389

Description

@taitelee

Summary

StreamController pushes every event onto an internal _buffer whenever no async-iterator waiter is pending. The primary documented consumption paths (subscribe() / liveQuery()) are callback-based and never drain that buffer, so it grows without bound; close() doesn't clear it either.

Detail

clients/ts/src/stream/controller.ts:

  • :27-38 onEvent delivers to callback subscribers, then if (waiter) waiter.resolve(...) else this._buffer.push(event).
  • Callback-only consumers never call [Symbol.asyncIterator]().next(), so _buffer.shift() is never reached.
  • close() (:150-163) resolves waiters but does not clear _buffer.

FilteredStreamController compounds this (inner + outer controllers each buffer).

Impact

A long-lived dashboard subscription on a busy table accumulates every event in memory until the tab dies.

Fix direction

Only buffer when at least one async-iterator consumer has been used, or cap/clear the buffer for callback-only mode; clear _buffer on close().

Found in a repo-wide audit; verified by code trace. #152 is the unrelated server-side buffer.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/sdkTypeScript SDK (clients/ts/)area/streamingSSE / live-query delivery path (/v1/stream)bugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions