Skip to content
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

don't cancel existing subscriptions when updating cache from another source #10597

Merged
merged 2 commits into from
Mar 29, 2023

Conversation

phryneas
Copy link
Member

@phryneas phryneas commented Feb 24, 2023

This PR fixes an issue first reproduced in #10587.

For an explanation of the bug, take a look at this commit (a test) (That test is not very visible in the overall diff since I reformatted the ObservableQuery test.)

Basically: when a cache update from another source was triggered, that other cache update discarded & unsubscribed the currently running Link - even if it only was reading from cache.

With this PR, concasts now contain an array of "descriptions", describing the actions they contain - a link request, an update from cache, or unknown.

This allows the logic in reobserve to only replace the current concast if the incoming data source was a link request - which should be a more sensible approach.

Unfortunately, this adds about 100b of bundle size - but especially with long-running queries with @defer in the future I don't see a way around this fix.

Checklist:

  • If this PR contains changes to the library itself (not necessary for e.g. docs updates), please include a changeset (see CONTRIBUTING.md)
  • If this PR is a new feature, please reference an issue where a consensus about the design was reached (not necessary for small changes)
  • Make sure all of the significant new logic is covered by tests

@phryneas phryneas requested a review from benjamn February 24, 2023 17:13
@changeset-bot
Copy link

changeset-bot bot commented Feb 24, 2023

🦋 Changeset detected

Latest commit: 84686b8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@apollo/client Patch

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

@netlify
Copy link

netlify bot commented Feb 24, 2023

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit 169840f
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/6422a1495948390008183fdb
😎 Deploy Preview https://deploy-preview-10597--apollo-client-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@phryneas
Copy link
Member Author

phryneas commented Mar 2, 2023

This could also have some overlap with #10614, so I'm mentioning that here so I don't forget to take a look at that in this context.

@phryneas phryneas marked this pull request as ready for review March 20, 2023 14:31
b: "",
},
},
// TODO: this should be `true`, but that seems to be a separate bug!
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like invalid behavior to me, but also is an "independent" bug.
I will try to handle this in a separate pull request.

Comment on lines +1249 to +1258
// there is just no way we can synchronously get the *right* value here,
// so we will assume `true`, which is the behaviour before the bug fix in
// #10597. This means that bug is not fixed in that case, and is probably
// un-fixable with reasonable effort for the edge case of @export as
// directives.
containsDataFromLink = true;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just the unfortunate truth - we need this information synchronously to act on it, and there is no way we can do that. So this PR has to exclude fixing that.

@phryneas
Copy link
Member Author

I managed to get this PR "out of the Concast", to have much more local impact - and less bundle size. As a result, some internal functions now return a tuple instead of a single value, but that seemed to be the "least impact" solution here.

@benjamn could you please take another look?

networkStatus: NetworkStatus,
): ConcastSourcesArray<ApolloQueryResult<TData>> {
networkStatus: NetworkStatus
): [containsDataFromLink: boolean, ...sources: ConcastSourcesArray<ApolloQueryResult<TData>>] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very glad to see we can keep this logic out of the Concast implementation, but I'm a little uneasy with combining distinct pieces of information into one tuple array, especially when one of those items is also an array (sources), and the other item is an unnamed boolean value (containsDataFromLink).

For readability and maintainability (like adding more info properties in the future), I think I would prefer returning an object with named properties instead of a tuple, even though this is private code that we fully control.

However, I do appreciate why a tuple might minify better than an object with named properties, so I did a quick investigation to see how much bundle size would be added by using objects instead of tuples. The object-based approach adds +172 bytes, still coming in under the current bundle size limit of 32.72KB, which I think is worth it for readability and simpler typing.

Since I've already done this experiment, rather than asking you to reproduce the results, I could just push my commit to this branch, if that's okay with you @phryneas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the commit: 18cb2f3

Comment on lines 1523 to 1534
return [false, resultsFromCache(diff, queryInfo.markReady())];
return { fromLink: false, sources: [resultsFromCache(diff, queryInfo.markReady())] };
}

if (returnPartialData || shouldNotify) {
return [true, resultsFromCache(diff), resultsFromLink()];
return { fromLink: true, sources: [resultsFromCache(diff), resultsFromLink()] };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally used a helper function here since I thought this repetitive code might be bad for bundle size, but it seems GZIP is able to compress the two repeated prefixes reasonably well:

return{fromLink:false,sources:[
return{fromLink:true,sources:[

Copy link
Member

@benjamn benjamn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with the current state of this PR if you are, @phryneas!

@@ -1,41 +1,39 @@
import gql from 'graphql-tag';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the commit that formats this file somewhere in .git-blame-ignore-revs? I didn't see at as part of this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is in here. But it's a good point, I'll have to rewrite history on this PR so it can be merged without squashing.

Copy link
Member

@jerelmiller jerelmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a couple of minor points of feedback, but this PR looks good otherwise. Thanks for getting this fixed! Looks like it was a tricky one to figure out 😄

"@apollo/client": patch
---

Fix a potential race condition between a link and parallel cache updates
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Fix a potential race condition between a link and parallel cache updates
Fix a race condition where parallel cache updates from multiple queries would sometimes result in incorrect `data`.

See if you like this a bit better. I think the "between a link" feels a bit too ambiguous, or at least I'm not sure it adequately describes the fix here. Seems like the "multiple queries" is important looking at your test. Feel free to tweak this a bit more, but just wanted something that might help a dev looking at our changelog understand what we fixed!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reworded it to "Fix a bug where an incoming cache update could prevent future updates from the active link."

).then(fromVariables)
: fromVariables(normalized.variables!)
);
let concast: Concast<ApolloQueryResult<TData>>, containsDataFromLink: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let concast: Concast<ApolloQueryResult<TData>>, containsDataFromLink: boolean;
let concast: Concast<ApolloQueryResult<TData>>,
containsDataFromLink: boolean;

I know this seems petty, but I honestly searched for a while before I realized this variable was defined in the same line as concast. For readability, could we put this on its own line to make it easier to see where this variable is created?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not petty, perfectly valid complaint :)

(cherry picked from commit 0a67647)
… from the active link.

Co-authored-by: Ben Newman <ben@apollographql.com>
@phryneas
Copy link
Member Author

@jerelmiller once you're happy with the wording, this is ready for the "Rebase and Merge" (we have one separate formatting commit) button :)

@phryneas
Copy link
Member Author

Well, I didn't hear any complaints about the new wording, so I'm merging :)

@phryneas phryneas merged commit 8fb9d19 into main Mar 29, 2023
@phryneas phryneas deleted the pr/explore-10587 branch March 29, 2023 08:53
@github-actions github-actions bot mentioned this pull request Mar 29, 2023
@jerelmiller
Copy link
Member

@phryneas whoops missed your ping. Sorry about that! It's good 😄

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants