feat(server): add fast disk-based snapshot reader - #975
Conversation
- add ISnapshotService that reads session state and wire log directly from disk for fast initial sync - gate the reader behind KIMI_SNAPSHOT_READER (auto/legacy) with a KIMI_SNAPSHOT_TIMEOUT_MS ceiling, keeping the legacy assembly as a fallback - version @moonshot-ai/server and other internal packages in changesets instead of ignoring them
🦋 Changeset detectedLatest commit: 11d2014 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1aa8cc607
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const [snapState, transcriptResult] = await Promise.all([ | ||
| this.broadcast.getSnapshotState(sid), | ||
| this._readTranscriptCached(sid, locator.sessionDir), | ||
| ]); |
There was a problem hiding this comment.
Don't advance the snapshot cursor past the disk transcript
When a client reconnects while a prompt is active, agent records are flushed to wire.jsonl asynchronously, but WSBroadcastService advances the durable seq as soon as events are published. This Promise.all can therefore return a snapState.seq that includes a durable event while _readTranscriptCached still sees the old wire file; the response then contains stale/missing messages but tells the client to subscribe from the newer as_of_seq. Since getBufferedSince replays only events with seq > cursor.seq, that skipped event/message is not recovered by the normal snapshot→subscribe flow.
Useful? React with 👍 / 👎.
| this._transcriptCache.delete(sid); | ||
| } | ||
|
|
||
| const transcript = await readWireTranscript(sessionDir, MAIN_AGENT_ID); |
There was a problem hiding this comment.
Apply wire migrations before reducing old transcripts
For sessions created with an older wire protocol and not already resumed in this daemon, this default path feeds raw wire.jsonl records directly into readWireTranscript. That helper is documented to be called after resumeSession because replay migrates outdated wire versions in place; bypassing that precondition means migration-dependent records such as v1.0 tool-call messages can be reduced with the wrong shape and produce incorrect snapshot messages, whereas the legacy MessageService resumed/migrated first.
Useful? React with 👍 / 👎.
commit: |
Use bracket access for process.env['KIMI_SNAPSHOT_READER'] to satisfy noPropertyAccessFromIndexSignature.
8aac974 to
11d2014
Compare
Related Issue
No linked issue — see Problem below.
Problem
GET /sessions/{session_id}/snapshotbuilds the initial-sync view by walking the heavy core RPC chain (resumeSession/getContext). Under load this exceeded 5s at p99, which trips the gateway's 5s cut-off and surfaces as a 499 to the client. There was no per-request ceiling, so a slow assembly could hang the route past the proxy timeout.What changed
Fast disk-based snapshot reader
state.jsonandwire.jsonlstraight from disk, bypassing the core RPC chain for the common case.KIMI_SNAPSHOT_READER(autoby default,legacyto fall back) with a hardKIMI_SNAPSHOT_TIMEOUT_MSceiling (default 4000ms, under the proxy cut-off). A timeout returns a structuredsnapshot.timeouterror instead of letting the gateway 499.legacyfallback, so this is a recoverable cut-over rather than a hard switch.Changesets config
@moonshot-ai/serverand the other internal packages in.changeset/config.jsonso they are versioned, and added a changeset so this release covers both the server change and the CLI bundle that ships it.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.