11// @flow
22
33import type { QueryJob } from "../query-runner"
4-
5- /**
6- * Jobs of this module
7- * - Ensure on bootstrap that all invalid page queries are run and report
8- * when this is done
9- * - Watch for when a page's query is invalidated and re-run it.
10- */
11-
124const _ = require ( `lodash` )
13-
14- const queue = require ( `./query-queue ` )
5+ const Queue = require ( `better-queue` )
6+ const convertHrtime = require ( `convert-hrtime ` )
157const { store, emitter } = require ( `../redux` )
8+ const queryQueue = require ( `./queue` )
169
1710let queuedDirtyActions = [ ]
1811
19- let active = false
20- let running = false
21-
2212const runQueriesForPathnamesQueue = new Set ( )
23- exports . queueQueryForPathname = pathname => {
13+ const queueQueryForPathname = pathname => {
2414 runQueriesForPathnamesQueue . add ( pathname )
2515}
2616
27- // Do initial run of graphql queries during bootstrap.
28- // Afterwards we listen "API_RUNNING_QUEUE_EMPTY" and check
29- // for dirty nodes before running queries.
30- exports . runInitialQueries = async ( ) => {
31- active = true
32- await runQueries ( true )
33- return
34- }
35-
36- const runQueries = async ( initial = false ) => {
37- // Don't run queries until bootstrap gets to "run graphql queries"
38- if ( ! active ) {
39- return
40- }
41-
17+ const calcQueries = ( initial = false ) => {
4218 // Find paths dependent on dirty nodes
4319 queuedDirtyActions = _ . uniq ( queuedDirtyActions , a => a . payload . id )
4420 const dirtyIds = findDirtyIds ( queuedDirtyActions )
@@ -71,13 +47,9 @@ const runQueries = async (initial = false) => {
7147
7248 runQueriesForPathnamesQueue . clear ( )
7349
74- // Run these paths
75- await runQueriesForPathnames ( pathnamesToRun )
76- return
50+ return pathnamesToRun
7751}
7852
79- exports . runQueries = runQueries
80-
8153emitter . on ( `CREATE_NODE` , action => {
8254 queuedDirtyActions . push ( action )
8355} )
@@ -86,26 +58,6 @@ emitter.on(`DELETE_NODE`, action => {
8658 queuedDirtyActions . push ( { payload : action . payload } )
8759} )
8860
89- const runQueuedActions = async ( ) => {
90- if ( active && ! running ) {
91- try {
92- running = true
93- await runQueries ( )
94- } finally {
95- running = false
96- if ( queuedDirtyActions . length > 0 ) {
97- runQueuedActions ( )
98- }
99- }
100- }
101- }
102- exports . runQueuedActions = runQueuedActions
103-
104- // Wait until all plugins have finished running (e.g. various
105- // transformer plugins) before running queries so we don't
106- // query things in a 1/2 finished state.
107- emitter . on ( `API_RUNNING_QUEUE_EMPTY` , runQueuedActions )
108-
10961let seenIdsWithoutDataDependencies = [ ]
11062
11163// Remove pages from seenIdsWithoutDataDependencies when they're deleted
@@ -147,10 +99,11 @@ const findIdsWithoutDataDependencies = () => {
14799 return notTrackedIds
148100}
149101
150- const runQueriesForPathnames = pathnames => {
102+ const makeQueryJobs = pathnames => {
151103 const staticQueries = pathnames . filter ( p => p . slice ( 0 , 4 ) === `sq--` )
152104 const pageQueries = pathnames . filter ( p => p . slice ( 0 , 4 ) !== `sq--` )
153105 const state = store . getState ( )
106+ const queryJobs = [ ]
154107
155108 staticQueries . forEach ( id => {
156109 const staticQueryComponent = store . getState ( ) . staticQueryComponents . get ( id )
@@ -162,16 +115,14 @@ const runQueriesForPathnames = pathnames => {
162115 componentPath : staticQueryComponent . componentPath ,
163116 context : { path : staticQueryComponent . jsonName } ,
164117 }
165- queue . push ( queryJob )
118+ queryJobs . push ( queryJob )
166119 } )
167120
168121 const pages = state . pages
169- let didNotQueueItems = true
170122 pageQueries . forEach ( id => {
171123 const page = pages . get ( id )
172124 if ( page ) {
173- didNotQueueItems = false
174- queue . push (
125+ queryJobs . push (
175126 ( {
176127 id : page . path ,
177128 jsonName : page . jsonName ,
@@ -186,18 +137,7 @@ const runQueriesForPathnames = pathnames => {
186137 )
187138 }
188139 } )
189-
190- if ( didNotQueueItems || ! pathnames || pathnames . length === 0 ) {
191- return Promise . resolve ( )
192- }
193-
194- return new Promise ( resolve => {
195- const onDrain = ( ) => {
196- queue . removeListener ( `drain` , onDrain )
197- resolve ( )
198- }
199- queue . on ( `drain` , onDrain )
200- } )
140+ return queryJobs
201141}
202142
203143const findDirtyIds = actions => {
@@ -221,3 +161,72 @@ const findDirtyIds = actions => {
221161 )
222162 return uniqDirties
223163}
164+
165+ const runInitialQueries = async activity => {
166+ const pathnamesToRun = calcQueries ( true )
167+ if ( pathnamesToRun . length === 0 ) {
168+ return
169+ }
170+
171+ const queryJobs = makeQueryJobs ( pathnamesToRun )
172+
173+ const queue = queryQueue . makeBuild ( )
174+
175+ const startQueries = process . hrtime ( )
176+ queue . on ( `task_finish` , ( ) => {
177+ const stats = queue . getStats ( )
178+ activity . setStatus (
179+ `${ stats . total } /${ stats . peak } ${ (
180+ stats . total / convertHrtime ( process . hrtime ( startQueries ) ) . seconds
181+ ) . toFixed ( 2 ) } queries/second`
182+ )
183+ } )
184+ await queryQueue . processBatch ( queue , queryJobs )
185+ }
186+
187+ /////////////////////////////////////////////////////////////////////
188+ // Listener for gatsby develop
189+
190+ // Initialized via `startListening`
191+ let listenerQueue
192+
193+ /**
194+ * Run any dirty queries. See `calcQueries` for what constitutes a
195+ * dirty query
196+ */
197+ const runQueuedQueries = ( ) => {
198+ if ( listenerQueue ) {
199+ listenerQueue . push ( makeQueryJobs ( calcQueries ( false ) ) )
200+ }
201+ }
202+
203+ /**
204+ * Starts a background process that processes any dirty queries
205+ * whenever one of the following occurs:
206+ *
207+ * 1. A node has changed (but only after the api call has finished
208+ * running)
209+ * 2. A component query (e.g by editing a React Component) has
210+ * changed
211+ *
212+ * For what constitutes a dirty query, see `calcQueries`
213+ */
214+ const startListening = queue => {
215+ // We use a queue to process batches of queries so that they are
216+ // processed consecutively
217+ listenerQueue = new Queue ( ( queryJobs , callback ) =>
218+ queryQueue
219+ . processBatch ( queue , queryJobs )
220+ . then ( ( ) => callback ( null ) )
221+ . catch ( callback )
222+ )
223+
224+ emitter . on ( `API_RUNNING_QUEUE_EMPTY` , runQueuedQueries )
225+ }
226+
227+ module . exports = {
228+ runInitialQueries,
229+ startListening,
230+ runQueuedQueries,
231+ queueQueryForPathname,
232+ }
0 commit comments