Apollo client executes for 1000 milliseconds on React-Native worker thread during single mutation
See original GitHub issueIntended outcome: Have a performant React Native application that performs mutations without locking up the JS thread.
Actual outcome: Apollo seems to block the main JS worker thread for ~1000ms any time I perform a mutation. It’s obviously doing lots of things, most of which seem to have to do with trying to read and write the cache, but it’s unclear to me why it would be necessary to do all this work for a single mutation, and why it is necessary to completely block the thread while doing so.
The following screenshot shows the “after-effects” of a single “updateJob” mutation. I’m using cache-and-network
, which does mean two responses to the mutation, but the responses to the mutation don’t come until the last 100 ms of this trace.
This is an example of the data item being updated, as returned from the mutation. example_job_pp_json.txt
How to reproduce the issue: Unfortunately I’m not clear on what, exactly, is going on in the bowels of the Apollo code, so I’m not sure how to reproduce it outside my own application. I’m simply executing a mutation and then watching as my app locks up for nearly a second. I’m hoping that these profiling screenshots will make it possible for someone who understands the codebase to at least theorize about it.
Versions System: OS: macOS 10.14.4 Binaries: Node: 8.11.3 - ~/.nvm/versions/node/v8.11.3/bin/node Yarn: 1.13.0 - ~/workspace/VisionNxMobile/node_modules/.bin/yarn npm: 6.2.0 - ~/.nvm/versions/node/v8.11.3/bin/npm Browsers: Chrome: 73.0.3683.86 Firefox: 65.0.1 Safari: 12.1 npmPackages: apollo: 2.4 => 2.4.4 apollo-client: ^2.4.13 => 2.4.13 react-apollo: ^2.2.4 => 2.3.2
Issue Analytics
- State:
- Created 4 years ago
- Reactions:11
- Comments:7 (3 by maintainers)
Top GitHub Comments
we just upgraded to React Native 0.57 from 0.56, which anecdotally seemed to make things worse (and is when we started digging in to this). But I have no real reason to believe that anecdote.
We only call the
updateJobMutation
once, but we do have anupdate
callback on that that is doing some manual cache updating that someone else wrote and that I suspect we don’t need. However, I’ve also tried pulling those out and the overall slowness still exists. I’ll see if I can find that branch and pull out a profile of the code without that extra update method.Yeah, the user-facing issue is definitely the synchronicity, though obviously it would be nice to consume less battery than we are. The thing is, we don’t get much advantage from Apollo’s bells and whistles (particularly the normalization), since we only deal with our data objects in business logic at a high level, as there are only meaningful IDs (from a backend perspective) at the top (Job) level. So partly I just wish we could turn off all the normalization and extra work and use the cache as a simple key-value store.
In the meantime, though, we guessed that having our major ListJobs-consuming component querying for the entire Job (which is simpler) might be causing Apollo to do extra work, so we did a manual prefetch of the Jobs and then changed that component to only request the specific fields it needs. That reduced our total JS Apollo runtime to about 400 ms, as seen in the following profile.
As you can see, the render portions are still less than half of the ~400ms overall runtime.
Here is a comparison profile of the same operation, but taken with the Hermes cache, where you can see that all but about 40ms of the synchronous call is taken up by our own (admittedly very unoptimized) render performance.
If anyone can provide the reproduction mentioned in https://github.com/apollographql/apollo-client/issues/4658#issuecomment-481036943, we’ll take a closer look. Thanks!