-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Reuse observables in client.watchFragment as much as possible
#13026
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reuse observables in client.watchFragment as much as possible
#13026
Conversation
🦋 Changeset detectedLatest commit: 0094d70 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
✅ Docs preview readyThe preview is ready to be viewed. View the preview File Changes 0 new, 6 changed, 0 removedBuild ID: a365b2291066c874ccd8443a URL: https://www.apollographql.com/docs/deploy-preview/a365b2291066c874ccd8443a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR optimizes watchFragment performance by reducing the number of observables created when watching the same cache items. Instead of creating a new observable chain each time client.watchFragment and cache.watchFragment are called, the implementation now reuses observables as much as possible by:
- Passing transform functions (for data masking) via a Symbol property rather than creating new observable chains via
pipe - Caching observables in
watchSingleFragmentand reusing them across multiple calls with the same parameters - Moving result transformation logic inside the cached observable rather than wrapping it externally
Key Changes
- Transform functions are now passed via
Symbol.for("apollo.transform")to enable observable reuse watchSingleFragmentnow includes transform application and result caching within the shared observable- Equality checks use
equalByQueryduring broadcasts to minimize updates while maintaining result stability
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/core/ApolloClient.ts | Refactored to pass masking transform via Symbol instead of piping observables, enabling direct reuse of cache observables |
| src/cache/core/cache.ts | Major refactoring of watchFragment and watchSingleFragment to cache and reuse observables, with transform application moved inside cached observables |
| .size-limits.json | Updated bundle size limits to reflect small increases from the new caching logic |
| .changeset/tiny-pumas-compete.md | Added changeset describing the performance improvement |
Comments suppressed due to low confidence (1)
src/cache/core/cache.ts:547
- Potential mutation of shared constant: When
ids.length === 0, the code usesemptyArrayObservable(a shared constant) and then callsObject.assign(observable, { getCurrentResult: ... })at line 549. This mutates the sharedemptyArrayObservableconstant, which could cause issues ifwatchFragmentis called multiple times with empty arrays, as they would all try to assign differentgetCurrentResultimplementations to the same object.
Consider creating a new observable wrapper or cloning emptyArrayObservable before assigning to it.
const observable =
ids.length === 0 ?
emptyArrayObservable
: combineLatestBatched(observables).pipe(
map(toResult),
tap({
subscribe: () => (subscribed = true),
unsubscribe: () => (subscribed = false),
}),
shareReplay({ bufferSize: 1, refCount: true })
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
See https://community.apollographql.com/t/many-repeat-cache-watches-causing-memory-cpu-overhead/9556
This PR aims to reduce the number of created observables and reuse as many as possible when watching the same item in the cache. Previously we'd create a new observable each time we called
client.watchFragmentandcache.watchFragmentdue to thepipeusage in each of these APIs.cache.watchFragmentcreated a chain of observables, that was then also chained byclient.watchFragmentto provide masking. Now we subscribe to the same observable as much as we can to reduce the amount of processing time between when the cache broadcast happens and the observable value is returned.