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.

runQuery filter in custom resolver breaks other queries (race condition?)

See original GitHub issue

I define a custom relationship from neighbourhoods to stores using createSchemaCustomization:

gatsby-node.js

exports.createSchemaCustomization = ({ actions, schema }) => {
  const { createTypes } = actions
  const { buildObjectType } = schema

  createTypes([
    buildObjectType({
      name: "SanityNeighbourhood",
      interfaces: ["Node"],
      fields: {
        stores: {
          type: "[SanityStore]",
          resolve: async ({ id }, args, { nodeModel }) =>
            await nodeModel.runQuery({
              query: {
                filter: { neighbourhoods: { elemMatch: { id: { eq: id } } } },
              },
              type: "SanityStore",
              firstOnly: false,
            }),
        },
      },
    }),
  ])
}

Then I create a page for each neighbourhood:

gatsby-node.js

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions

  const { data, errors } = await graphql(`
    query CreatePages {
      neighbourhoods: allSanityNeighbourhood {
        nodes {
          slug {
            current
          }
        }
      }
    }
  `)

  if (errors) {
    throw errors
  }

  data.neighbourhoods.nodes.forEach(({ slug: { current: slug } }) => {
    createPage({
      path: slug,
      component: require.resolve(`./src/templates/neighbourhood.js`),
      context: { slug },
    })
  })
}

My neighbourhood template calls my custom resolver to retrieve a list of stores for each neighbourhood:

src/templates/neighbourhood.js

export const query = graphql`
  query NeighbourhoodTemplate($slug: String!) {
    sanityNeighbourhood(slug: { current: { eq: $slug } }) {
      stores {
        id
      }
    }
  }
`

So far, everything works.

Now, on my home page, if I try to grab a list of stores in a particular neighbourhood:

src/pages/index.js

export const query = graphql`
  query IndexPage {
    allSanityStore(
      filter: {
        neighbourhoods: {
          elemMatch: { slug: { current: { eq: "harbourfront" } } }
        }
      }
    ) {
      nodes {
        id
      }
    }
  }
`

allSanityStore.nodes comes back empty. If I make the exact same query at runtime using GraphiQL, I get the expected list of nodes.

For some reason, my custom resolver has affected the result of a seemingly unrelated query.

This is just one example. It seems that any custom resolver that calls nodeModel.runQuery with a filter seems to have unpredictable, far-reaching impacts on query results across the entire site.

Steps to reproduce

See https://github.com/gatsbyjs/gatsby/issues/26056#issuecomment-675508616

Expected result

Defining a custom resolver should not affect the results of unrelated queries.

Actual result

My custom resolver affects the results of unrelated queries.

Environment

  System:
    OS: Linux 4.19 Debian GNU/Linux 9 (stretch) 9 (stretch)
    CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    Shell: 4.4.12 - /bin/bash
  Binaries:
    Node: 12.18.3 - /usr/local/bin/node
    Yarn: 1.22.4 - /usr/bin/yarn
    npm: 6.14.6 - /usr/local/bin/npm
  Languages:
    Python: 2.7.13 - /usr/bin/python
  npmPackages:
    gatsby: ^2.24.47 => 2.24.47
    gatsby-source-sanity: ^6.0.3 => 6.0.3

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:13 (13 by maintainers)

github_iconTop GitHub Comments

1reaction
aaronadamsCAcommented, Aug 18, 2020

@vladar, you should be able to clone this to reproduce:

https://github.com/shopnishe/gatsby-issue-26056

It’s using a public Sanity dataset. The index page should be displaying a list of store IDs, but for me it’s blank. If you can populate it with the same set of IDs as the other two pages I linked (a template page and a GraphiQL query), I believe you’ve fixed the bug.

I’ve also updated the ticket description with a straightforward explanation of the use case described in the reproduction repository.

Let me know if you need anything more. Thanks!

1reaction
vladarcommented, Aug 12, 2020

Some context on what’s going on under the hood: when Gatsby sees filter or sort on fields with custom resolvers - it executes those resolvers before running a query. Then it puts resolved values to the node object itself (to special __gatsby_resolved property).

It works fine until you use runQuery inside your custom resolvers that affects the same fields.

My hypothesis is that this is what happens:

  1. Gatsby runs resolvers when starting GraphQL query and puts results into __gatsby_resolved property on the node object itself

  2. One of your custom resolvers runs runQuery and the result is replacing __gatsby_resolved property

That’s why we see conflicts. In other words, the problem is that the query mutates the node itself (to enable search/sorting on those “dynamic” properties). And then the next query sees values that were set by the previous query.

That’s a hypothesis as it is indeed hard to catch because when you debug it queries may be executed in a different order. It’s only visible when a certain race condition occurs.

To confirm - try replacing runQuery call with custom filtering on all nodes. We really want to catch this issue (as I’ve seen similar reports in the past but also couldn’t reproduce reliably).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Writing query resolvers | Full-Stack Quickstart - Apollo GraphQL
A resolver is a function that's responsible for populating the data for a single field in your schema. Whenever a client queries for...
Read more >
Prisma Client API (Reference)
See Filter conditions and operators for examples of how to filter results. Get all User records where the name is Alice.
Read more >
Race condition like behavior in Apollo Resolvers
We've been having some strange issues with field resolution using Apollo federation --. We have a type defined in Service A called ObjectA...
Read more >
OmniSci Release Notes - docs
Selecting text transforms the “Run query” button to “Run selected”. ... Fixed an issue where invalid custom SQL filters would create a dashboard-level...
Read more >
Gatsby - Runquery Inside Createresolver Using Group
+ - **gatsby:** fix broken GraphQL resolver tracing For additional information on how ... filter in custom resolver breaks other queries (race condition?)...
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