Skip to content

ClientSession.snapshot() has no size or event ceiling, so a server-side caller cannot bound what it pulls #2317

Description

@sloops77

What problem are you trying to solve?

ClientSession.snapshot() is the way to read of a session's durable log. #1051 was closed NOT_PLANNED as its bounded-read request was superseded by #1219 and #1404: "session.stream({ follow: false }) and ClientSession.snapshot() now pin the durable tail and return a cursor for exact live resumption."

However It only pins the tail:

snapshot(e) {
  e?.signal?.throwIfAborted();
  let t = [];
  for await (let n of this.#o({ follow: false, signal: e?.signal, startIndex: 0 })) t.push(n);
  return { events: t, session: { sessionId: ..., streamIndex: t.length } };
}

Always startIndex: 0, accumulate every event, no byte or event limit. The only control is signal, which bounds duration, not memory consumed within it. Memory consumption arguably matters even more than the event count, because of the amplification #1051 documented: every message.appended carries the whole message so far and each one is persisted, so a message's stored size is quadratic in its appends. #1051's repro measured a session stream at "100-1000× the transcript size". #944 reports a downstream session with 17,194 message.appended events.

So a caller reading a snapshot has no way to say "give up rather than pull more than N events or bytes". The failure mode is a server-side render pulling and retaining a multi-hundred-megabyte transcript, with no signal until it happens.

Proposed solution

An optional bound on snapshot(), rejecting once exceeded such as

await session.snapshot({ signal, maxBytes: 5 * 1024 * 1024 });

A distinct error type (SnapshotTooLargeError, or ClientError with a discriminant) would let callers fall back to a partial transcript, or render without history

Alternatives considered

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions