Move chat-history persistence to append-only JSONL - #2431
Draft
SasinduDilshara wants to merge 4 commits into
Draft
Move chat-history persistence to append-only JSONL#2431SasinduDilshara wants to merge 4 commits into
SasinduDilshara wants to merge 4 commits into
Conversation
Threads previously persisted as a single whole-file JSON snapshot (thread.json) that was re-serialized and rewritten on every mutation. Because the file grows with the conversation and the write is synchronous, per-turn cost scaled with total history (O(n²) over a session), causing noticeable lag in long chats. Switch to an append-only log (thread.jsonl): each mutation appends one record instead of rewriting everything, so adding a turn costs the same whether the conversation is 2 or 200 messages long. On load the records are replayed to rebuild the thread; the log is compacted (amortized, and on load) to reclaim superseded records. - file-utils: appendLineSync + readJsonlSync (tolerant of a torn trailing line for crash safety) - types: ThreadLogRecord union (head/meta/gen/del/trunc) - schema-migration: bump thread schema to v2 with an identity v1->v2 migration (data shape unchanged, storage format differs) - persistence-store: append API (appendGeneration/removeGenerationRecord/ truncateFromGeneration/updateThreadMeta), replay-based loadThread with one-time thread.json -> thread.jsonl migration + legacy delete, compacted saveThread, compactThread, amortized + on-load compaction - tests: full coverage of append/replay/tombstone/truncate/torn-line/ migration/compaction; existing suite updated for the new format Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…writes (wso2#1861) Adversarial self-review follow-ups: - Amortized compaction is now size-proportional (compact after max(64, 3*liveGens) appends, not a fixed 100), so per-append cost stays genuinely O(1) amortized instead of growing with thread size — the O(n²) shape the issue targets is fully removed, not just reduced. - On-load compaction (compactOnLoadIfNeeded) and append-time compaction (noteAppend) now swallow write errors. loadThread became a writer on the happy path; an unguarded transient disk error there would have aborted restore of the entire workspace even though the thread was already replayed in memory. Compaction is a pure optimization and must never fail a load or an append. - loadThread falls back to a legacy thread.json when the jsonl exists but is empty/unrecoverable, instead of reporting the thread as gone. Tests: +3 (size-aware threshold, read-only-dir load resilience, empty-jsonl legacy fallback); 72 pass / 0 fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) Follow-up to reviewer feedback: the previous design re-appended the whole generation (its entire, growing modelMessages) on every agent step and relied on compaction to clean up the duplicates — roughly O(S^2) bytes written per S-step turn, all synchronous. Switch to message-level records so each message is written exactly once: - `gen` — a lightweight generation header (everything except modelMessages), appended only when a header field actually changes. - `msg` — one model message, appended as it arrives. - `msgs` — replace a generation's whole message list (only when the messages are rewritten rather than extended, e.g. server-side compaction; detected cheaply via length/last-message change). appendGeneration now diffs against per-generation tracking and writes only the delta (new messages + header-if-changed), so a turn costs O(messages) to persist instead of O(messages^2). Replay groups `msg` records by genId in order; compaction/save emit one `msg` per message. The consumer (ChatStateStorage) is unchanged — it still calls appendGeneration with the full generation; the delta logic lives entirely in the store. Tests: +4 (message-once / no-duplication, header-only-on-change, msgs reset, headless message reconstruction); 75 pass / 0 fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (4)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
SasinduDilshara
marked this pull request as draft
July 27, 2026 14:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Solution
Moves Copilot chat-history persistence from full-file JSON rewrites to an append-only JSONL log in
copilot-utilities.appendGeneration/removeGenerationRecord/truncateFromGeneration); full rewrites are kept only for thread creation and one-time legacy migration.Fixes wso2/product-integrator#1861
🤖 Generated with Claude Code