Repository navigation
fix(core): implement append-only delta patching and bounded history windowing in ChatRecordingService - #29568
Conversation
…indowing in ChatRecordingService
- Add ToolCallPatch, MessagePatch, and MessagePatchRecord ($patch) in chatRecordingTypes.ts and deprecate passing full messages arrays in MetadataUpdateRecord.
- Update updateMessagesFromHistory() in chatRecordingService.ts to append new turns as individual MessageRecord lines, emit $rewindTo for pure tail rollbacks, and emit targeted $patch records for content, tool call result, and ordering updates.
- Extend loadConversationRecord and loadConversationRecordSync via createJsonlRecordAccumulator to process $patch records while maintaining backward compatibility with legacy .json and $set: { messages } session files.
- Replace per-turn JSON.stringify comparisons across historical turns with WeakRef reference checks paired with a compact deterministic digest (computeContentDigest).
- Bound in-memory cachedConversation.messages to MAX_HISTORY_MESSAGES (50) and reconstruct complete conversation records on demand in getConversation() and rewindTo() when older messages have been evicted.
- Add unit and regression tests in chatRecordingService.test.ts covering delta patching, linear O(n) disk scaling, bounded in-memory windowing, and session resumption/rewind.
Fixes google-gemini#28357
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This PR optimizes the ChatRecordingService by moving from full-history serialization to an incremental, append-only delta patching model. By implementing a bounded in-memory sliding window and efficient content fingerprinting, the service significantly reduces disk usage, heap consumption, and synchronization latency, effectively resolving performance bottlenecks in long-running chat sessions. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
📊 PR Size: size/XL
|
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to the ChatRecordingService to optimize disk usage and limit in-memory message caching to MAX_HISTORY_MESSAGES. It replaces full-history checkpoints with incremental $patch and $rewindTo records, and implements a lightweight indexing mechanism to track message and tool call changes. A critical review comment points out a potential data desynchronization issue in updateMessagesFromHistory when this.hasEvictedMessages is true, where reordering or removing messages in the active trailing window requires reloading the full conversation from disk to maintain correctness.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to the ChatRecordingService to optimize disk usage and memory footprint. Instead of rewriting the entire message history using full checkpoints, the service now appends lightweight delta patches ($patch) and rewind records ($rewindTo), while limiting the in-memory cache to MAX_HISTORY_MESSAGES. The loadConversationRecord function has been refactored to use a stream-based accumulator that reconstructs the full conversation from these incremental records. Feedback was provided regarding an unsafe reference-equality optimization in updateFingerprintIfChanged that could lead to silent data corruption, suggesting a fallback to direct content hash comparison instead.
88998 Footnotes
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to ChatRecordingService to optimize memory usage and disk space. Instead of rewriting the entire message history, it now appends atomic delta patches and bounds the in-memory cache to MAX_HISTORY_MESSAGES. Feedback on these changes highlights a critical correctness bug in the mixString sampling logic, which can miss in-place mutations of large strings and cause silent data corruption, and a performance issue where getConversation performs synchronous disk I/O that can block the event loop.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to ChatRecordingService to optimize disk usage and limit in-memory message caching to MAX_HISTORY_MESSAGES. Feedback on these changes highlights two critical areas for improvement: first, sorting object keys in computeContentDigest to ensure deterministic fingerprinting and prevent redundant disk writes; second, implementing a lazy-loaded cache for reconstructed conversations in getConversation() to avoid blocking the Node.js event loop with repeated synchronous file reads.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to ChatRecordingService to optimize memory usage and disk serialization. It bounds the in-memory cached messages to MAX_HISTORY_MESSAGES and writes incremental $patch and $rewindTo records to the JSONL session file instead of rewriting the entire message history. A review comment correctly identifies that the current implementation of orderChanged evaluates to true on every message append, resulting in redundant $patch records and O(n^2) disk overhead, and suggests a fix to detect pure appends.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to the ChatRecordingService to optimize disk space usage and limit in-memory message caching to MAX_HISTORY_MESSAGES. It replaces full-history rewrites with incremental $patch records, utilizes content fingerprinting to detect in-place mutations, and adds robust support for legacy checkpoints. Feedback on the changes suggests optimizing performance and reducing garbage collection pressure in updateMessagesFromHistory by executing cache updates only when message content has actually changed.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces append-only delta patching and memory bounding to the ChatRecordingService to optimize memory usage and ensure disk file size scales linearly. It limits the in-memory cached messages to MAX_HISTORY_MESSAGES and writes incremental updates using a new $patch record format instead of rewriting the entire message history. Evicted messages are lazily reloaded from disk when full conversation reconstruction or rewinding is required. Additionally, comprehensive unit tests have been added to validate these memory-bounding and delta-patching behaviors. I have no feedback to provide as there are no review comments.
Summary
This change updates
ChatRecordingService(packages/core/src/services/chatRecordingService.tsandpackages/core/src/services/chatRecordingTypes.ts) to replace full-history{ $set: { messages } }rewrites and unbounded in-memory message retention with incremental append-only delta patching, constant-time change detection, and bounded in-memory message windowing.From an architectural perspective, this ensures linear$O(n)$ disk serialization across multi-turn sessions, eliminates repeated deep
JSON.stringifyscans across historical turns and tool call payloads duringupdateMessagesFromHistory(), and bounds the resident in-memorycachedConversation.messagesbuffer to a sliding window (MAX_HISTORY_MESSAGES = 50) while preserving full history reconstruction on demand from the backing.jsonlsession log.Details
Problem
Previously,
updateMessagesFromHistory()inChatRecordingServiceappended{ $set: { messages: newMessages } }on every turn, serializing the full message history to disk repeatedly ($O(n^2)$). For a100-turn session with tool calls, this created single lines exceeding 8.2 million characters and bloated the session file up to ~400 MB, causing V8 heap exhaustion (reaching up to 7+ GB in long sessions,
b/561554829).
Solution
$patch): Migrated to atomic updates ($patch) for mutated tool responses/turn parts, deprecating full-history$set: { messages }appends.JSON.stringifycomparisons withWeakRef+ fast content hash fingerprints (ContentFingerprint).cachedConversation.messagestoMAX_HISTORY_MESSAGES, offloading older history to disk and streaming on-demand for resume/rewind.Benchmark Results (100 Turns, ~80 KB Tool Output / Turn)
main)b_561554829).jsonl)Related Issues
How to Validate
ChatRecordingServiceunit and regression test suite:npm test -w @google/gemini-cli-core -- src/services/chatRecordingService.test.tsPre-Merge Checklist