question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Memory Leak - InMemoryCache and Optimism never deletes records

See original GitHub issue

In a project I work on (used by milions of people) we use Electron and in every pop-out window, we pass a reference to Window object within an operation’s context. Because the callback holds a reference to QueryInfo => ObservableQuery => ObservableQuery.options => options.context => context.window we have a memory leak. Consumers open new windows every few minutes (it’s a chat app) which adds more and more instances of Window.

How to reproduce the issue:

Repository | Deployed app

Here, we use dep from optimism package that creates a Map: https://github.com/apollographql/apollo-client/blob/481b30d5f90132d60ac479ce23ab4fd61acb3db5/src/cache/inmemory/inMemoryCache.ts#L356

When there’s a new query, InMemoryCache runs broadcastWatch method in which it uses optimism. The watchDep.dirty(c) removes a record from the Map

https://github.com/apollographql/apollo-client/blob/481b30d5f90132d60ac479ce23ab4fd61acb3db5/src/cache/inmemory/inMemoryCache.ts#L375

Later on in broadcastWatch, the watchDep(c) is called and it sets a new record in our Map:

https://github.com/apollographql/apollo-client/blob/481b30d5f90132d60ac479ce23ab4fd61acb3db5/src/cache/inmemory/inMemoryCache.ts#L383

The problem is that c which represents Cache.WatchOptions

https://github.com/apollographql/apollo-client/blob/481b30d5f90132d60ac479ce23ab4fd61acb3db5/src/cache/core/types/Cache.ts#L25-L28

is stored forever in the Map created in Optimism’s dep() and that callback is function that depends on QueryInfo class

https://github.com/apollographql/apollo-client/blob/481b30d5f90132d60ac479ce23ab4fd61acb3db5/src/core/QueryInfo.ts#L246-L251

which depends directly on ObservableQuery class in which we store query’s options operation’s context.

Versions

  System:
    OS: macOS 10.15.6
  Binaries:
    Node: 10.18.0 - ~/.nvm/versions/node/v10.18.0/bin/node
    Yarn: 1.22.4 - ~/.yarn/bin/yarn
    npm: 6.13.4 - ~/.nvm/versions/node/v10.18.0/bin/npm
  Browsers:
    Chrome: 85.0.4183.121
    Edge: 85.0.564.63
    Firefox: 79.0
    Safari: 13.1.2
  npmPackages:
    @apollo/client: 3.2.0 => 3.2.0

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:6 (5 by maintainers)

github_iconTop GitHub Comments

4reactions
benjamncommented, Oct 13, 2020

Thanks to @dotansimha’s reproduction and @kamilkisiela’s proposed solution, I’m confident this leak is fixed in @apollo/client@3.2.4 and @apollo/client@3.3.0-beta.12. Thanks for digging into these implementation details and correctly identifying the underlying problem, @kamilkisiela!

2reactions
dotansimhacommented, Oct 10, 2020

@benjamn I believe this issue should get more attention. Since we migrated a huge project to v3, we see multiple memory leaks. The one @kamilkisiela demonstrated above is only one, we are chasing those and it’s not easy to find. Here’s an example for a heap snapshot. It’s using Electron and opens a new Window. Eventually, we have Detached Window(s) since Apollo keeps records of the query params in memory, and never deletes it.

image

Here’s another heap snapshot of a memory leak we chase, and caused by retained context from observableQuery:

image

I think the 2 issues above might be related, and caused by the same issue, and @kamilkisiela 's issue is just one symptom of it. Both seems to be related to watchDep and the way it keeps it’s records.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Advanced topics on caching in Apollo Client
This article describes special cases and considerations when using the Apollo Client cache. Bypassing the cache. Sometimes you shouldn't use the cache for...
Read more >
52411 - AJAX Memory Leak - Chrome Never Releases ...
Chrome never releases the memory. What is the expected result? Chrome should release memory associated with old AJAX requests. What happens ...
Read more >
Python in-memory cache with time to live - Stack Overflow
The user function is cached using @functools.lru_cache with support for maxsize and typed parameters. The Result object records the function's return value and ......
Read more >
Top 10 most common Java performance problems
In C/C++, a memory leak occurs when you allocate memory, assign the address of that memory to a reference variable, and then delete...
Read more >
Cache Implementations in C# .NET | Michael's Coding Spot
There are 3 types of caches: In-Memory Cache is used for when you want to implement cache in a single process. When the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found