@@ -230,6 +230,7 @@ class QueryPlanningTaversal<RV extends Vertex> {
230
230
readonly startFetchIdGen : number ,
231
231
readonly hasDefers : boolean ,
232
232
readonly variableDefinitions : VariableDefinitions ,
233
+ private readonly statistics : PlanningStatistics | undefined ,
233
234
private readonly startVertex : RV ,
234
235
private readonly rootKind : SchemaRootKind ,
235
236
readonly costFunction : CostFunction ,
@@ -445,6 +446,10 @@ class QueryPlanningTaversal<RV extends Vertex> {
445
446
debug . log ( ( ) => `Reduced plans to consider to ${ planCount } plans` ) ;
446
447
}
447
448
449
+ if ( this . statistics ) {
450
+ this . statistics . evaluatedPlanCount += planCount ;
451
+ }
452
+
448
453
debug . log ( ( ) => `All branches:${ this . closedBranches . map ( ( opts , i ) => `\n${ i } :${ opts . map ( ( opt => `\n - ${ simultaneousPathsToString ( opt ) } ` ) ) } ` ) } ` ) ;
449
454
450
455
// Note that usually, we'll have a majority of branches with just one option. We can group them in
@@ -528,6 +533,7 @@ class QueryPlanningTaversal<RV extends Vertex> {
528
533
0 ,
529
534
false ,
530
535
this . variableDefinitions ,
536
+ undefined ,
531
537
edge . head ,
532
538
'query' ,
533
539
this . costFunction ,
@@ -2012,6 +2018,10 @@ function withSiblingTypenameOptimizedAway(operation: Operation): Operation {
2012
2018
) ;
2013
2019
}
2014
2020
2021
+ export type PlanningStatistics = {
2022
+ evaluatedPlanCount : number ,
2023
+ }
2024
+
2015
2025
export function computeQueryPlan ( {
2016
2026
config,
2017
2027
supergraphSchema,
@@ -2022,14 +2032,21 @@ export function computeQueryPlan({
2022
2032
supergraphSchema : Schema ,
2023
2033
federatedQueryGraph : QueryGraph ,
2024
2034
operation : Operation ,
2025
- } ) : QueryPlan {
2035
+ } ) : {
2036
+ plan : QueryPlan ,
2037
+ statistics : PlanningStatistics ,
2038
+ } {
2026
2039
if ( operation . rootKind === 'subscription' ) {
2027
2040
throw ERRORS . UNSUPPORTED_FEATURE . err (
2028
2041
'Query planning does not currently support subscriptions.' ,
2029
2042
{ nodes : [ parse ( operation . toString ( ) ) ] } ,
2030
2043
) ;
2031
2044
}
2032
2045
2046
+ const statistics : PlanningStatistics = {
2047
+ evaluatedPlanCount : 0 ,
2048
+ } ;
2049
+
2033
2050
const reuseQueryFragments = config . reuseQueryFragments ?? true ;
2034
2051
let fragments = operation . selectionSet . fragments
2035
2052
if ( fragments && reuseQueryFragments ) {
@@ -2062,7 +2079,10 @@ export function computeQueryPlan({
2062
2079
debug . group ( ( ) => `Computing plan for\n${ operation } ` ) ;
2063
2080
if ( operation . selectionSet . isEmpty ( ) ) {
2064
2081
debug . groupEnd ( 'Empty plan' ) ;
2065
- return { kind : 'QueryPlan' } ;
2082
+ return {
2083
+ plan : { kind : 'QueryPlan' } ,
2084
+ statistics,
2085
+ } ;
2066
2086
}
2067
2087
2068
2088
const root = federatedQueryGraph . root ( operation . rootKind ) ;
@@ -2086,6 +2106,7 @@ export function computeQueryPlan({
2086
2106
processor,
2087
2107
root,
2088
2108
deferConditions,
2109
+ statistics,
2089
2110
} )
2090
2111
} else {
2091
2112
rootNode = computePlanInternal ( {
@@ -2095,11 +2116,15 @@ export function computeQueryPlan({
2095
2116
processor,
2096
2117
root,
2097
2118
hasDefers,
2119
+ statistics,
2098
2120
} ) ;
2099
2121
}
2100
2122
2101
2123
debug . groupEnd ( 'Query plan computed' ) ;
2102
- return { kind : 'QueryPlan' , node : rootNode } ;
2124
+ return {
2125
+ plan : { kind : 'QueryPlan' , node : rootNode } ,
2126
+ statistics,
2127
+ } ;
2103
2128
}
2104
2129
2105
2130
function computePlanInternal ( {
@@ -2109,16 +2134,18 @@ function computePlanInternal({
2109
2134
processor,
2110
2135
root,
2111
2136
hasDefers,
2137
+ statistics,
2112
2138
} : {
2113
2139
supergraphSchema : Schema ,
2114
2140
federatedQueryGraph : QueryGraph ,
2115
2141
operation : Operation ,
2116
2142
processor : FetchGroupProcessor < PlanNode | undefined , DeferredNode >
2117
2143
root : RootVertex ,
2118
2144
hasDefers : boolean ,
2145
+ statistics : PlanningStatistics ,
2119
2146
} ) : PlanNode | undefined {
2120
2147
if ( operation . rootKind === 'mutation' ) {
2121
- const dependencyGraphs = computeRootSerialDependencyGraph ( supergraphSchema , operation , federatedQueryGraph , root , hasDefers ) ;
2148
+ const dependencyGraphs = computeRootSerialDependencyGraph ( supergraphSchema , operation , federatedQueryGraph , root , hasDefers , statistics ) ;
2122
2149
let allMain : ( PlanNode | undefined ) [ ] = [ ] ;
2123
2150
let allDeferred : DeferredNode [ ] = [ ] ;
2124
2151
let primarySelection : SelectionSet | undefined = undefined ;
@@ -2143,7 +2170,7 @@ function computePlanInternal({
2143
2170
deferred : allDeferred ,
2144
2171
} ) ;
2145
2172
} else {
2146
- const dependencyGraph = computeRootParallelDependencyGraph ( supergraphSchema , operation , federatedQueryGraph , root , 0 , hasDefers ) ;
2173
+ const dependencyGraph = computeRootParallelDependencyGraph ( supergraphSchema , operation , federatedQueryGraph , root , 0 , hasDefers , statistics ) ;
2147
2174
const { main, deferred } = dependencyGraph . process ( processor ) ;
2148
2175
return processRootNodes ( {
2149
2176
processor,
@@ -2162,13 +2189,15 @@ function computePlanForDeferConditionals({
2162
2189
processor,
2163
2190
root,
2164
2191
deferConditions,
2192
+ statistics,
2165
2193
} : {
2166
2194
supergraphSchema : Schema ,
2167
2195
federatedQueryGraph : QueryGraph ,
2168
2196
operation : Operation ,
2169
2197
processor : FetchGroupProcessor < PlanNode | undefined , DeferredNode >
2170
2198
root : RootVertex ,
2171
2199
deferConditions : SetMultiMap < string , string > ,
2200
+ statistics : PlanningStatistics ,
2172
2201
} ) : PlanNode | undefined {
2173
2202
return generateConditionNodes (
2174
2203
operation ,
@@ -2181,6 +2210,7 @@ function computePlanForDeferConditionals({
2181
2210
processor,
2182
2211
root,
2183
2212
hasDefers : true ,
2213
+ statistics,
2184
2214
} ) ,
2185
2215
) ;
2186
2216
}
@@ -2296,6 +2326,7 @@ function computeRootParallelDependencyGraph(
2296
2326
root : RootVertex ,
2297
2327
startFetchIdGen : number ,
2298
2328
hasDefer : boolean ,
2329
+ statistics : PlanningStatistics ,
2299
2330
) : FetchDependencyGraph {
2300
2331
return computeRootParallelBestPlan (
2301
2332
supergraphSchema ,
@@ -2305,6 +2336,7 @@ function computeRootParallelDependencyGraph(
2305
2336
root ,
2306
2337
startFetchIdGen ,
2307
2338
hasDefer ,
2339
+ statistics ,
2308
2340
) [ 0 ] ;
2309
2341
}
2310
2342
@@ -2316,6 +2348,7 @@ function computeRootParallelBestPlan(
2316
2348
root : RootVertex ,
2317
2349
startFetchIdGen : number ,
2318
2350
hasDefers : boolean ,
2351
+ statistics : PlanningStatistics ,
2319
2352
) : [ FetchDependencyGraph , OpPathTree < RootVertex > , number ] {
2320
2353
const planningTraversal = new QueryPlanningTaversal (
2321
2354
supergraphSchema ,
@@ -2324,10 +2357,11 @@ function computeRootParallelBestPlan(
2324
2357
startFetchIdGen ,
2325
2358
hasDefers ,
2326
2359
variables ,
2360
+ statistics ,
2327
2361
root ,
2328
2362
root . rootKind ,
2329
2363
defaultCostFunction ,
2330
- emptyContext
2364
+ emptyContext ,
2331
2365
) ;
2332
2366
const plan = planningTraversal . findBestPlan ( ) ;
2333
2367
// Getting no plan means the query is essentially unsatisfiable (it's a valid query, but we can prove it will never return a result),
@@ -2358,16 +2392,17 @@ function computeRootSerialDependencyGraph(
2358
2392
federatedQueryGraph : QueryGraph ,
2359
2393
root : RootVertex ,
2360
2394
hasDefers : boolean ,
2395
+ statistics : PlanningStatistics ,
2361
2396
) : FetchDependencyGraph [ ] {
2362
2397
const rootType = hasDefers ? supergraphSchema . schemaDefinition . rootType ( root . rootKind ) : undefined ;
2363
2398
// We have to serially compute a plan for each top-level selection.
2364
2399
const splittedRoots = splitTopLevelFields ( operation . selectionSet ) ;
2365
2400
const graphs : FetchDependencyGraph [ ] = [ ] ;
2366
2401
let startingFetchId : number = 0 ;
2367
- let [ prevDepGraph , prevPaths ] = computeRootParallelBestPlan ( supergraphSchema , splittedRoots [ 0 ] , operation . variableDefinitions , federatedQueryGraph , root , startingFetchId , hasDefers ) ;
2402
+ let [ prevDepGraph , prevPaths ] = computeRootParallelBestPlan ( supergraphSchema , splittedRoots [ 0 ] , operation . variableDefinitions , federatedQueryGraph , root , startingFetchId , hasDefers , statistics ) ;
2368
2403
let prevSubgraph = onlyRootSubgraph ( prevDepGraph ) ;
2369
2404
for ( let i = 1 ; i < splittedRoots . length ; i ++ ) {
2370
- const [ newDepGraph , newPaths ] = computeRootParallelBestPlan ( supergraphSchema , splittedRoots [ i ] , operation . variableDefinitions , federatedQueryGraph , root , prevDepGraph . nextFetchId ( ) , hasDefers ) ;
2405
+ const [ newDepGraph , newPaths ] = computeRootParallelBestPlan ( supergraphSchema , splittedRoots [ i ] , operation . variableDefinitions , federatedQueryGraph , root , prevDepGraph . nextFetchId ( ) , hasDefers , statistics ) ;
2371
2406
const newSubgraph = onlyRootSubgraph ( newDepGraph ) ;
2372
2407
if ( prevSubgraph === newSubgraph ) {
2373
2408
// The new operation (think 'mutation' operation) is on the same subgraph than the previous one, so we can concat them in a single fetch
0 commit comments