runQuery filter in custom resolver breaks other queries (race condition?)
See original GitHub issueI 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:
- Created 3 years ago
- Reactions:1
- Comments:13 (13 by maintainers)
Top GitHub Comments
@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!
Some context on what’s going on under the hood: when Gatsby sees
filter
orsort
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:
Gatsby runs resolvers when starting GraphQL query and puts results into
__gatsby_resolved
property on the node object itselfOne of your custom resolvers runs
runQuery
and the result is replacing__gatsby_resolved
propertyThat’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).