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

Fix regression that causes partial data to be reported unexpectedly in some circumstances #11579

Merged
merged 33 commits into from Feb 15, 2024

Conversation

jerelmiller
Copy link
Member

@jerelmiller jerelmiller commented Feb 8, 2024

Fixes #11400

Fixes a regression introduced by #8642 in 2d5e498 that causes partial data to be reported by useQuery/useLazyQuery when one query fails and another succeeds with overlapping data. This is specifically triggered when notifyOnNetworkStatus is set to true on the first failing query. The cache write from the second query triggers the first query to try and refetch since there is missing field data in that query. Because notifyOnNetworkStatus was set to true, there was a render that contained additional partial data since this was set on the lastResult by the cache broadcast.

This PR adjusts the QueryInfo.setDiff function to ignore cache results that contain partial data when returnPartialData is set to false.

Important

Previously queries with errors would auto refetch when a partial cache update was written that overlapped with the query. This is no longer the case since partial cache updates are ignored and not reported.

Copy link

changeset-bot bot commented Feb 8, 2024

🦋 Changeset detected

Latest commit: 1e408bd

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

Copy link
Contributor

github-actions bot commented Feb 8, 2024

size-limit report 📦

Path Size
dist/apollo-client.min.cjs 38.16 KB (+0.09% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" 45.97 KB (+0.06% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" (production) 43.51 KB (+0.08% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" 33.9 KB (+0.11% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" (production) 31.82 KB (+0.11% 🔺)
import { ApolloProvider } from "dist/react/index.js" 1.23 KB (0%)
import { ApolloProvider } from "dist/react/index.js" (production) 1.21 KB (0%)
import { useQuery } from "dist/react/index.js" 5.2 KB (0%)
import { useQuery } from "dist/react/index.js" (production) 4.27 KB (0%)
import { useLazyQuery } from "dist/react/index.js" 5.5 KB (0%)
import { useLazyQuery } from "dist/react/index.js" (production) 4.58 KB (0%)
import { useMutation } from "dist/react/index.js" 3.51 KB (0%)
import { useMutation } from "dist/react/index.js" (production) 2.73 KB (0%)
import { useSubscription } from "dist/react/index.js" 3.19 KB (0%)
import { useSubscription } from "dist/react/index.js" (production) 2.38 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" 5.28 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" (production) 3.94 KB (0%)
import { useBackgroundQuery } from "dist/react/index.js" 4.75 KB (0%)
import { useBackgroundQuery } from "dist/react/index.js" (production) 3.4 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" 4.97 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" (production) 3.63 KB (0%)
import { useReadQuery } from "dist/react/index.js" 3.04 KB (0%)
import { useReadQuery } from "dist/react/index.js" (production) 2.98 KB (0%)
import { useFragment } from "dist/react/index.js" 2.18 KB (0%)
import { useFragment } from "dist/react/index.js" (production) 2.13 KB (0%)

Copy link

netlify bot commented Feb 8, 2024

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit 449b6a2
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/65cce81a2297b70008281d1b
😎 Deploy Preview https://deploy-preview-11579--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 configuration.

@jerelmiller jerelmiller changed the title Failing test for #11400 Fix regression from 3.4.8 that caused partial data to be reported unexpectedly in some circumstances Feb 9, 2024
@jerelmiller jerelmiller changed the title Fix regression from 3.4.8 that caused partial data to be reported unexpectedly in some circumstances Fix regression that causes partial data to be reported unexpectedly in some circumstances Feb 9, 2024
@jerelmiller jerelmiller marked this pull request as ready for review February 9, 2024 00:31
@jerelmiller jerelmiller requested a review from a team as a code owner February 9, 2024 00:31
Comment on lines 251 to 257
// QueryInfo calls cache.watch with returnPartialData set to `true` always,
// which means that cache broadcasts for some queries will store the
// lastResult with partial data even though we don't tolerate it.
if (result.partial && !this.options.returnPartialData) {
result.data = void 0 as any;
}

Copy link
Member

Choose a reason for hiding this comment

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

I'd love it if this were the solution, but this will reset deferred results in the second call to getCurrentResult.

Here is a failing test:

    it.only("should handle multiple calls to getCurrentResult without losing data", async () => {
      const query = gql`
        {
          greeting {
            message
            ... on Greeting @defer {
              recipient {
                name
              }
            }
          }
        }
      `;

      const link = new MockSubscriptionLink();

      const client = new ApolloClient({
        link,
        cache: new InMemoryCache(),
      });

      const obs = client.watchQuery({ query });
      const stream = new ObservableStream(obs);

      setTimeout(() => {
        link.simulateResult({
          result: {
            data: {
              greeting: {
                message: "Hello world",
                __typename: "Greeting",
              },
            },
            hasNext: true,
          },
        });
      });

      {
        const result = await stream.takeNext();
        expect(result.data).toEqual({
          greeting: {
            message: "Hello world",
            __typename: "Greeting",
          },
        });
        expect(obs.getCurrentResult().data).toEqual({
          greeting: {
            message: "Hello world",
            __typename: "Greeting",
          },
        });
        // second call to `getCurrentResult`
        expect(obs.getCurrentResult().data).toEqual({
          greeting: {
            message: "Hello world",
            __typename: "Greeting",
          },
        });
      }
    });

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you. I was going to dig more into possible fail scenarios today since @defer uses partial. Thanks for accelerating my findings 🙂

Copy link

Choose a reason for hiding this comment

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

We ran into this issue after updating the package, and releasing it yesterday. Thank you for working on this fix!

@jerelmiller jerelmiller marked this pull request as draft February 9, 2024 16:01
@jerelmiller jerelmiller force-pushed the jerel/issue-11400-unexpected-partial-data branch from b169071 to 5b1853d Compare February 12, 2024 19:59
@jerelmiller jerelmiller marked this pull request as ready for review February 12, 2024 21:49
@jerelmiller jerelmiller marked this pull request as draft February 13, 2024 00:14
@jerelmiller
Copy link
Member Author

/release:pr

Copy link
Contributor

A new release has been made for this PR. You can install it with npm i @apollo/client@0.0.0-pr-11579-20240213222138.

@jerelmiller jerelmiller marked this pull request as ready for review February 13, 2024 22:41
Copy link
Member

@phryneas phryneas left a comment

Choose a reason for hiding this comment

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

This looks viable to me. Let's talk in our 1:1 later about any problems that you see with the approach.

src/core/QueryInfo.ts Outdated Show resolved Hide resolved
src/core/QueryInfo.ts Outdated Show resolved Hide resolved
});
}

await expect(Profiler).not.toRerender();
Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think it should be mentioned in the changelog.
Maybe add a second changeset to the PR and also classify it as a bug-fix/patch. It's probably better to have as a separate point.

@jerelmiller
Copy link
Member Author

@phryneas this should be good to go. I've addressed all the comments and am happy with the diff in this PR. Appreciate the suggestions!

Copy link
Member

@phryneas phryneas left a comment

Choose a reason for hiding this comment

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

Then let's get this in :)

@github-actions github-actions bot added the auto-cleanup 🤖 label Feb 15, 2024
@jerelmiller jerelmiller merged commit 1ba2fd9 into main Feb 15, 2024
33 checks passed
@jerelmiller jerelmiller deleted the jerel/issue-11400-unexpected-partial-data branch February 15, 2024 16:23
@github-actions github-actions bot mentioned this pull request Feb 15, 2024
@av-k
Copy link

av-k commented Mar 14, 2024

If during handling a response is found an error (for example a missed field described inside "typePolicies" for cache), when "useQuery" won't be triggered after the mutation gets the update.

The same code that works in 3.9.4 after the update to 3.9.5 won't work.

FYI @jerelmiller

@jerelmiller
Copy link
Member Author

@av-k do you have a code sample on what you mean? To be honest, I'm having a bit of a difficult time visualizing what you're saying.

Are you perhaps experiencing the thing mentioned at the bottom of the PR description?

Previously queries with errors would auto refetch when a partial cache update was written that overlapped with the query. This is no longer the case since partial cache updates are ignored and not reported.

@av-k
Copy link

av-k commented Mar 15, 2024

@jerelmiller PR description? - it's not about the mentioned problem.

In order to provide a code sample, I will require a demo GraphQL server with a schema containing nested objects within an entity.

So, to be able to reproduce this issue just get an error during handling the response, so here:
should be:

{
  "diff": {
    "complete": false,
    "missing": [Error: Can't find field 'blablabla' on ENTITY_ID_1 object at new MissingFieldError....],
    "result": {"root": {...}}
  },
  "olddiff": {
    "complete": false,
    "missing": [Error: Can't find field 'blablabla' on ENTITY_ID_1 object at new MissingFieldError....],
    "result": {"root": {...}}
  }
}

So if there is an error (for example the incorrect configuration of "typePolicies") - "diff.complete" will be always "false".

@phryneas
Copy link
Member

@av-k
Ich glaube wir verstehen hier einfach das Problem nicht, weil die Fehlerbeschreibung sehr kurz ist.

Koennten Sie das Problem vielleicht noch einmal etwas ausfuehrlicher auf Deutsch beschreiben?
Vielleicht klappt das besser :)

@av-k
Copy link

av-k commented Mar 15, 2024

@phryneas Thank you for the suggestion, but I will still try :)

Unfortunately, I was unable to achieve an error regarding the missing field using the codesandbox example, so I reproduced a simpler case with a field merge error in "typePolicies" (index.js:22).

I'm attaching two identical codesandboxes, the only difference being the versions of @apollo/client.

@apollo/client v.3.9.5
https://codesandbox.io/p/sandbox/issue-apollo-client-v-3-9-5-notes-app-vk8jlf

@apollo/client v.3.9.4
https://codesandbox.io/p/sandbox/issue-apollo-client-v-3-9-5-notes-app-forked-d3923z

Steps to reproduce:

  1. optional, restart the backend codesandbox if it is needed
  2. Visit the sandbox.
  3. Add a Todo.
  4. Update the added Todo.

Actual behavior (in version 3.9.5 - broken):
After updating the Todo, the label of the Todo is NOT updated.

Expected behavior (in version 3.9.4 - works):
After updating the Todo, the label of the Todo is updated.

P.S. At first glance, it may seem strange why I presented a code example where I intentionally broke cache management functionality. There are two main concerns on my part:

  1. Applications running smoothly but likely having inaccuracies in cache configuration (typePolicies) up to version 3.9.4 inclusively worked seamlessly.
  2. Given that there was an error in the configuration of field merge settings in "typePolicies", it remains impossible to easily detect the error. Neither the developer console nor the application provides any information about the problem whatsoever.

@av-k
Copy link

av-k commented Apr 5, 2024

@jerelmiller is there any updates?
It was possible to reproduce the problem on the provided codesandboxes?

@jerelmiller
Copy link
Member Author

Hey @av-k apologies I lost track of this. I've moved this to #11759 which will be easier to track.

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

Successfully merging this pull request may close these issues.

useQuery can unexpectedly return partial data
4 participants