question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Two queries loop forever when requesting different parts of same object

See original GitHub issue

Intended outcome: I have a CRUD-like application where the user may create, read update or delete objects of different types. Let’s say we are talking about “countries” and “cities”. I can then execute queries like this:

countries { id name }

or this:

cities { id name }

However, different users have different permissions to work with these objects, and the server owns the logic about this. To indicate to the React app whether Create, Edit or Delete buttons should be visible for the current user and a particular object type, there is a “permissions” object available in the GraphQL schema.

Then we can make queries like this

permissions { countries cities }

…and get back lists of enum values (CREATE, READ, UPDATE, DELETE). An example response for the query above is:

permissions { countries: ['READ'], cities: ['CREATE', 'READ', 'UPDATE'] }

If a specific React component is handling Countries, it may then make the following query:

countries { id name } permissions { countries }

…and another component handling Cities may execute this query:

cities { id name } permissions { cities }

Actual outcome:

This worked fine until 3.0.0-beta.46. But now, at least when two queries like those mentioned above are executed simultaneously, there is in an infinite loop of queries against the graphql server.

The problem seems to be around the permissions object. My conslusion is that the cache is having trouble when different parts of the object are coming in via different queries, for an object that has no identity.

The issue goes away if I do any of the following:

  • Remove one of the queries
  • Remove the permissions part of at least one of the queries
  • Expand the permissions part so all queries request the complete permissions object
  • downgrade to a version less than 3.0.0-beta.46

How to reproduce the issue: I’ve uploaded a repro project here: https://github.com/nordvall/apollo-query-loop

In the real app I can see the queries running off in the Networing tab in Chrome. In the repro project I’ve included some console logging, so you can see the looping in the Console tab instead.

The real server is running GraphQL.NET, so I’ve tried to replicate the schema in the Apollo resolver object model.

Versions @apollo/client: 3.0.0-beta.53 graphql: 14.6.0 react ^16.12.0

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:5
  • Comments:14 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
benjamncommented, Jun 1, 2020

@nordvall Can you tell me more about this Permissions type?

I can see that the desired behavior is for the fields of this Permissions object to be merged over time, so you don’t lose data fetched by different queries. Is that because the Permissions object is conceptually a global singleton object, so it’s always safe to merge new fields into it? Or should the merging of Permissions object fields apply only to objects requested via the root query { permissions } field?

To achieve the first behavior (global singleton), you can give Permissions objects a constant identity with keyFields: []:

const client = new ApolloClient({
  cache: new InMemoryCache({
    typePolicies: {
      Permissions: {
        // Interpretation: Permissions objects are normalized, but they're all
        // the same logical object, because their identity does not depend on
        // any of their fields (other than __typename).
        keyFields: [],
      },
    },
  }),
  link
});

To achieve the second behavior (merge only query { permissions } objects when they collide):

const client = new ApolloClient({
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          permissions: {
            // Interpretation: normally unidentified objects overwrite each
            // other when they collide, but we can opt into merging their fields
            // if we know that's safe:
            merge(existing, incoming, { mergeObjects }) {
              return mergeObjects(existing, incoming);
            },
          },
        },
      },
    },
  }),
  link
});

Either one of these approaches will stop the mutual clobbering of data that’s triggering the refetching. Obviously it would be ideal to detect and warn about situations like this, but I want to be sure the possible solutions that we would recommend in those warning messages actually meet your needs.

2reactions
benjamncommented, Jun 16, 2020

Judging by the provided reproduction (thanks!), @apollo/client@3.0.0-rc.5 should fix the endless network request loop, thanks to #6448.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Prevent infinite loop between objects referencing each other?
The simplest way I can think of is to create a flattened list. Recursively iterate the objects and store each reference in a...
Read more >
Loops: while(), for() and do .. while() - Physics and Astronomy
Here we learn about loops which allow sections of code to run zero or more times, ... If the condition is false the...
Read more >
Don't Block the Event Loop (or the Worker Pool) - Node.js
While a thread is blocked working on behalf of one client, it cannot handle requests from any other clients. This provides two motivations...
Read more >
Redux Essentials, Part 8: RTK Query Advanced Patterns
RTK Query network requests. RTK Query allows multiple components to subscribe to the same data, and will ensure that each unique set of...
Read more >
CS50W - Lecture 6 - User Interfaces - CS50 Video Player
... 0:01:19has multiple different pages, we've generally · 0:01:22done that via ... 1:20:27have to repeat myself for all of the parts of the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found