Growing InMemory store over time

We’re having memory issues in our app and noticed that the Apollo InMemoryCache store seems to grow quite large when coupled with subscriptions. Most of the items stored there aren’t of any use after being consumed and there aren’t any references to them anymore.

Looking at the documentation I can see that I can manually call cache.gc() to clear anything without a reference which would help, but then I notice this paragraph:

Apollo Client 3 enables you to selectively remove cached data that is no longer useful. The default garbage collection strategy of the gc method is suitable for most applications, but the evict method provides more fine-grained control for applications that require it.

  • What is this default garbage collection strategy?
  • Does it automatically run garbage collection when it gets so big?
  • Is there a setInterval somewhere that periodically calls it?
  • Should I need to care about doing that myself manually?

It would be great to document what the default behaviour is.

Hi @intellix :wave: Thanks for your questions.

To answer them in order:

  • (1-3) There is no “default gc strategy” in the sense that there is no setInterval periodically calling cache.gc(); garbage collection/cache eviction can be managed by the application developer by invoking cache.gc()/cache.evict({ id: 'my-object-id' })
  • (4) If your subscriptions are creating entries in the cache that are no longer reachable from a root object (and can therefore be discarded), you can free up that memory by calling cache.gc() in your application code

The “default garbage collection strategy of the gc method” is referring to the default effect of calling cache.gc() without any options; garbage collection behaves differently if resetResultCache: true or resetResultIdentities: true is passed in the options argument (more information on these options can be found in the docs).

I hope this is helpful, let me know if anything isn’t clear :slight_smile:

1 Like

ok, so it sounds like we have a lot of garbage collection to do as we never run it on a busy site full of subscriptions where players sit for hours. Do you have a recommendation perhaps for how you’d typically run GC?

Would there be any problem with doing a simple interval every 60 seconds to clear garbage?

setInterval(() => cache.gc(), 60_000)

Or is there a more recommended way to do it fine-grained? Like if we have a chat of 20x messages, is there a way to clear messages after they’ve been added/processed to the chat list?

updateQuery: (prev, { subscriptionData }) => {
  return {
    ...prev,
    messageConnection: {
      ...prev.messageConnection,
      edges: [{
        __typename: 'MessageEdge',
        node: subscriptionData.data.createMessage.message,
      }, ...prev.messageConnection.edges.slice(0, 20)],
    },
  };
}

It was honestly a surprise that I needed to manually run garbage collection. I’ve never seen a single person in any team I’ve been in suggest it. Think on the subscriptions documentation it would be a nice note to include that it’s something worth looking out for

@intellix you may be interested in this GraphQL Summit talk which gets pretty in-depth into garbage collection:

1 Like