1
1
// @flow
2
2
3
3
import 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
-
12
4
const _ = require ( `lodash` )
13
-
14
- const queue = require ( `./query-queue ` )
5
+ const Queue = require ( `better-queue` )
6
+ const convertHrtime = require ( `convert-hrtime ` )
15
7
const { store, emitter } = require ( `../redux` )
8
+ const queryQueue = require ( `./queue` )
16
9
17
10
let queuedDirtyActions = [ ]
18
11
19
- let active = false
20
- let running = false
21
-
22
12
const runQueriesForPathnamesQueue = new Set ( )
23
- exports . queueQueryForPathname = pathname => {
13
+ const queueQueryForPathname = pathname => {
24
14
runQueriesForPathnamesQueue . add ( pathname )
25
15
}
26
16
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 ) => {
42
18
// Find paths dependent on dirty nodes
43
19
queuedDirtyActions = _ . uniq ( queuedDirtyActions , a => a . payload . id )
44
20
const dirtyIds = findDirtyIds ( queuedDirtyActions )
@@ -71,13 +47,9 @@ const runQueries = async (initial = false) => {
71
47
72
48
runQueriesForPathnamesQueue . clear ( )
73
49
74
- // Run these paths
75
- await runQueriesForPathnames ( pathnamesToRun )
76
- return
50
+ return pathnamesToRun
77
51
}
78
52
79
- exports . runQueries = runQueries
80
-
81
53
emitter . on ( `CREATE_NODE` , action => {
82
54
queuedDirtyActions . push ( action )
83
55
} )
@@ -86,26 +58,6 @@ emitter.on(`DELETE_NODE`, action => {
86
58
queuedDirtyActions . push ( { payload : action . payload } )
87
59
} )
88
60
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
-
109
61
let seenIdsWithoutDataDependencies = [ ]
110
62
111
63
// Remove pages from seenIdsWithoutDataDependencies when they're deleted
@@ -147,10 +99,11 @@ const findIdsWithoutDataDependencies = () => {
147
99
return notTrackedIds
148
100
}
149
101
150
- const runQueriesForPathnames = pathnames => {
102
+ const makeQueryJobs = pathnames => {
151
103
const staticQueries = pathnames . filter ( p => p . slice ( 0 , 4 ) === `sq--` )
152
104
const pageQueries = pathnames . filter ( p => p . slice ( 0 , 4 ) !== `sq--` )
153
105
const state = store . getState ( )
106
+ const queryJobs = [ ]
154
107
155
108
staticQueries . forEach ( id => {
156
109
const staticQueryComponent = store . getState ( ) . staticQueryComponents . get ( id )
@@ -162,16 +115,14 @@ const runQueriesForPathnames = pathnames => {
162
115
componentPath : staticQueryComponent . componentPath ,
163
116
context : { path : staticQueryComponent . jsonName } ,
164
117
}
165
- queue . push ( queryJob )
118
+ queryJobs . push ( queryJob )
166
119
} )
167
120
168
121
const pages = state . pages
169
- let didNotQueueItems = true
170
122
pageQueries . forEach ( id => {
171
123
const page = pages . get ( id )
172
124
if ( page ) {
173
- didNotQueueItems = false
174
- queue . push (
125
+ queryJobs . push (
175
126
( {
176
127
id : page . path ,
177
128
jsonName : page . jsonName ,
@@ -186,18 +137,7 @@ const runQueriesForPathnames = pathnames => {
186
137
)
187
138
}
188
139
} )
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
201
141
}
202
142
203
143
const findDirtyIds = actions => {
@@ -221,3 +161,72 @@ const findDirtyIds = actions => {
221
161
)
222
162
return uniqDirties
223
163
}
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