Skip to content

perf(core): fix OOM crash in long-running sessions#21207

Closed
jacob314 wants to merge 1 commit intomainfrom
perf/fix-oom-long-running-sessions
Closed

perf(core): fix OOM crash in long-running sessions#21207
jacob314 wants to merge 1 commit intomainfrom
perf/fix-oom-long-running-sessions

Conversation

@jacob314
Copy link
Contributor

@jacob314 jacob314 commented Mar 5, 2026

Replace structuredClone() in getHistory() with a shallow array copy.
The deep clone ran on every turn's hot path, duplicating the entire
conversation history (50-100MB+ in long sessions) multiple times per
turn. Verified all 8 production callers — only one mutated Content
objects (nextSpeakerChecker), and that mutation was dead code (pushed
to a clone that was immediately discarded).

Replace Array.shift() in the event backlog with a head pointer and
amortized compaction. shift() on a 10K-element array is O(n); the
new approach is O(1) amortized with identical FIFO semantics.

Cache Turn.getResponseText() result to avoid redundant recomputation
across its two call sites per turn.

Fixes #19607

fix(core): remove unknown cast for backlog

Summary

Details

Related Issues

How to Validate

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

Replace structuredClone() in getHistory() with a shallow array copy.
The deep clone ran on every turn's hot path, duplicating the entire
conversation history (50-100MB+ in long sessions) multiple times per
turn. Verified all 8 production callers — only one mutated Content
objects (nextSpeakerChecker), and that mutation was dead code (pushed
to a clone that was immediately discarded).

Replace Array.shift() in the event backlog with a head pointer and
amortized compaction. shift() on a 10K-element array is O(n); the
new approach is O(1) amortized with identical FIFO semantics.

Cache Turn.getResponseText() result to avoid redundant recomputation
across its two call sites per turn.

Fixes #19607

fix(core): remove unknown cast for backlog
@jacob314 jacob314 requested a review from a team as a code owner March 5, 2026 03:52
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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 pull request significantly enhances the performance and memory efficiency of core chat functionalities. By transitioning from deep cloning to shallow copying for conversation history, it resolves critical OOM issues in extended sessions. Additionally, it optimizes event backlog management and introduces caching for response text retrieval, leading to a more robust and responsive user experience, especially in long-running conversations.

Highlights

  • Optimized History Retrieval: Replaced structuredClone() with a shallow array copy in getHistory() to prevent deep cloning of conversation history, which caused Out-Of-Memory (OOM) crashes in long sessions.
  • Improved Event Backlog Performance: Switched Array.shift() with a head pointer and amortized compaction for the event backlog, changing its complexity from O(n) to O(1) amortized.
  • Cached Response Text: Implemented caching for Turn.getResponseText() to avoid redundant recomputation, as it is called multiple times per turn.
Changelog
  • packages/core/src/core/geminiChat.ts
    • Modified getHistory() to return a shallow array copy instead of a deep clone.
  • packages/core/src/core/turn.ts
    • Implemented caching for the getResponseText() method to prevent redundant computations.
Activity
  • No activity has been recorded for this pull request yet.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize 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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant performance improvements to address an out-of-memory crash in long-running sessions. Replacing the deep structuredClone() with a shallow array copy in getHistory() is an excellent optimization that directly targets the source of the memory pressure. The addition of caching to getResponseText() is also a good, clean optimization. I have one suggestion to enhance the type safety of the getHistory method, ensuring its contract is enforced at compile time to prevent potential state corruption issues.

Comment on lines +694 to +700
// Return a shallow copy of the array to prevent callers from mutating
// the internal history array (push/pop/splice). Content objects are
// shared references — callers MUST NOT mutate them in place.
// This replaces a prior structuredClone() which deep-copied the entire
// conversation on every call, causing O(n) memory pressure per turn
// that compounded into OOM crashes in long-running sessions.
return [...history];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The move to a shallow copy is a great performance optimization that resolves the OOM issue. However, relying solely on a comment to prevent callers from mutating the shared Content objects is fragile and could lead to hard-to-debug state corruption down the line.

To enforce this contract at compile time without any runtime performance penalty, you can leverage TypeScript's Readonly types. This provides a much stronger guarantee against accidental mutations.

I recommend updating the function signature to return a ReadonlyArray<Content>:

getHistory(curated: boolean = false): ReadonlyArray<Content> {

This change will prevent methods like .push() or direct index assignments on the returned array, making the implementation significantly safer.

getHistory(curated: boolean = false): ReadonlyArray<Content> {

@jacob314 jacob314 closed this Mar 5, 2026
@jacob314 jacob314 deleted the perf/fix-oom-long-running-sessions branch March 5, 2026 04:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Long-running sessions crash with OOM due to structuredClone and O(n) event backlog eviction

2 participants