Apollo Client cache not feasible for large data sets?

Hey :slight_smile:

We’re using Apollo Client, and are in general satisfied. However, we’ve run into performance issues for some of our clients that have very large accounts, which leads to some large requests and store caches.

The UI will stutter and drop frames to a varying degree, but it’s very noticeable. It seems to increase/occur whenever a request comes in and tries to update the cache.

Based on discussion here: Slow updates with large cache

we’ve looked into our cache sizes and limits, and have adjusted accordingly.

We seem to have a pretty large cache, but have adjusted the limits to accommodate it:

{
    "limits": {
        "parser": 1000,
        "canonicalStringify": 1000,
        "print": 2000,
        "documentTransform.cache": 2000,
        "queryManager.getDocumentInfo": 2000,
        "PersistedQueryLink.persistedQueryHashes": 2000,
        "fragmentRegistry.transform": 2000,
        "fragmentRegistry.lookup": 1000,
        "fragmentRegistry.findFragmentSpreads": 4000,
        "cache.fragmentQueryDocuments": 1000,
        "removeTypenameFromVariables.getVariableDefinitions": 2000,
        "inMemoryCache.maybeBroadcastWatch": 5000,
        "inMemoryCache.executeSelectionSet": 250000,
        "inMemoryCache.executeSubSelectedArray": 150000
    },
    "sizes": {
        "print": 26,
        "parser": 38,
        "canonicalStringify": 15,
        "links": [],
        "queryManager": {
            "getDocumentInfo": 27,
            "documentTransforms": []
        },
        "cache": {
            "fragmentQueryDocuments": 0
        },
        "addTypenameDocumentTransform": [
            {
                "cache": 27
            }
        ],
        "inMemoryCache": {
            "executeSelectionSet": 128121,
            "executeSubSelectedArray": 92948,
            "maybeBroadcastWatch": 49
        },
        "fragmentRegistry": {}
    }
}

It should be normalized, at least to a very high degree. We also have lint rules to ensure that everywhere an ID field is available, it is included in the query fragment on that type.

Any advice on how we can improve performance for the client/cache while managing large data sets would be much appreciated.

Thanks!

Hey @jhgrund :wave:

Have you taken any performance profiles to see where the the time is spent? Its difficult to recommend anything without knowing where the bottleneck is.

Sure, I just found it very hard to get anything useful out of except a lot of store operations and recomputes.

There’s a bunch of long running tasks of many seconds in the profile, and they are so deeply nested a screenshot doesn’t do it justice.

Here’s a short video from one of the 4 second long running tasks from a profile:

Google Photos

Here’s a screenshot from another perf slice:

The store seems to go into a loop or something somehow, causing the entire page to hang. Is that a symptom of something we do wrong somewhere?

It can only be reproduced for our clients with large hierarchies

The request that kicks off the most recalculations from the store is a very simple one

export const LATEST_TIMESTAMP_QUERY = gql(/* GraphQL */ `
  query LatestValueReceivedForInstallation($installationId: ID!) {
    node(id: $installationId) {
      ... on Installation {
        id
        name
        dataLastReceivedAt
      }
    }
  }
`);

It only receives id and string scalars on a single node, no nested types or anything.

Can you maybe send one of those profiles to lenz@apollographql.com ? From just the screenshot it’s impossible to tell anything here.

Would love to, but I can’t save them, chrome throws error saying

Failed to save timeline: Invalid string length (RangeError)

when I try to save the profile. Apparently, after some googling, it’s too large to save, even if I tried to make the profile short in length

I’ve tried screen recording the trace a bit for you here:

I’ve also found this (old) bug in the mean time, but can’t tell if it was ever solved or just auto closed?

The simple query I linked uses fetchPolicy: 'cache-and-network' and we’re using "@apollo/client": "3.12.2"

Memory also climbs throughout the trace

That issue was related to memoization cache sizes, and while I agree that your problem here looks like it would hit a memoization cache limit, in your initial post you show a level of usage that doesn’t.

Maybe you can try to profile with Firefox instead or record a replay.io replay?
I really can’t read anything for half of that video and would need to look around in that data myself.

Sent you a performance profile from Firefox :+1:

Hmm. This solidifies my gut feeling that you might be hitting memoization cache limits.

Which gives me an idea: getMemoryInternals only shows the limits that would be in place if the cache would be created now - but not necessarily the limits that were in place when the cache was created. Could it be that you set these limits too late and they don’t get picked up?