Skip to content

Commit

Permalink
perf(gatsby): cache parsing and validation results in graphql-runner (#…
Browse files Browse the repository at this point in the history
…20477)

* perf(gatsby): cache parsing and validation results in graphql-runner

* Fix broken test
  • Loading branch information
vladar authored and GatsbyJS Bot committed Jan 17, 2020
1 parent 6dfffb6 commit ac7c79f
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 21 deletions.
19 changes: 12 additions & 7 deletions packages/gatsby/src/bootstrap/__tests__/graphql-runner.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
jest.mock(`graphql`)

const createGraphqlRunner = require(`../graphql-runner`)
const { graphql } = require(`graphql`)
const { execute, validate, parse } = require(`graphql`)

parse.mockImplementation(() => {
return {}
})
validate.mockImplementation(() => [])

const createStore = (schema = {}) => {
return {
Expand Down Expand Up @@ -33,9 +38,9 @@ describe(`grapqhl-runner`, () => {
gatsby: `is awesome`,
},
}
graphql.mockImplementation(() => Promise.resolve(expectation))
execute.mockImplementation(() => Promise.resolve(expectation))

const result = await graphqlRunner({}, {})
const result = await graphqlRunner(``, {})
expect(reporter.panicOnBuild).not.toHaveBeenCalled()
expect(result).toBe(expectation)
})
Expand All @@ -51,9 +56,9 @@ describe(`grapqhl-runner`, () => {
},
],
}
graphql.mockImplementation(() => Promise.resolve(expectation))
execute.mockImplementation(() => Promise.resolve(expectation))

const result = await graphqlRunner({}, {})
const result = await graphqlRunner(``, {})
expect(reporter.panicOnBuild).not.toHaveBeenCalled()
expect(result).toBe(expectation)
})
Expand All @@ -68,13 +73,13 @@ describe(`grapqhl-runner`, () => {
message: `Cannot query field boyhowdy on RootQueryType`,
}

graphql.mockImplementation(() =>
execute.mockImplementation(() =>
Promise.resolve({
errors: [errorObject],
})
)

await graphqlRunner({}, {})
await graphqlRunner(``, {})
expect(reporter.panicOnBuild).toHaveBeenCalled()
expect(reporter.panicOnBuild).toMatchSnapshot()
})
Expand Down
73 changes: 59 additions & 14 deletions packages/gatsby/src/query/graphql-runner.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { graphql } = require(`graphql`)
const { parse, validate, execute } = require(`graphql`)
const { debounce } = require(`lodash`)

const withResolverContext = require(`../schema/context`)
const { LocalNodeModel } = require(`../schema/node-model`)
Expand All @@ -16,24 +17,68 @@ class GraphQLRunner {
schemaComposer: schemaCustomization.composer,
createPageDependency,
})
this.schema = schema
this.parseCache = new Map()
this.validDocuments = new WeakSet()
this.scheduleClearCache = debounce(this.clearCache.bind(this), 5000)
}

clearCache() {
this.parseCache.clear()
this.validDocuments = new WeakSet()
}

parse(query) {
if (!this.parseCache.has(query)) {
this.parseCache.set(query, parse(query))
}
return this.parseCache.get(query)
}

validate(schema, document) {
if (!this.validDocuments.has(document)) {
const errors = validate(schema, document)
if (!errors.length) {
this.validDocuments.add(document)
}
return errors
}
return []
}

query(query, context) {
const { schema, schemaCustomization } = this.store.getState()

return graphql(
schema,
query,
context,
withResolverContext({
schema,
schemaComposer: schemaCustomization.composer,
context,
customContext: schemaCustomization.context,
nodeModel: this.nodeModel,
}),
context
)
if (this.schema !== schema) {
this.schema = schema
this.clearCache()
}

const document = this.parse(query)
const errors = this.validate(schema, document)

const result =
errors.length > 0
? { errors }
: execute({
schema,
document,
rootValue: context,
contextValue: withResolverContext({
schema,
schemaComposer: schemaCustomization.composer,
context,
customContext: schemaCustomization.context,
nodeModel: this.nodeModel,
}),
variableValues: context,
})

// Queries are usually executed in batch. But after the batch is finished
// cache just wastes memory without much benefits.
// TODO: consider a better strategy for cache purging/invalidation
this.scheduleClearCache()
return result
}
}

Expand Down

0 comments on commit ac7c79f

Please sign in to comment.