Skip to content

Commit 004f68e

Browse files
authored
feat: compute static costs (#1359)
First release of essential features for the static cost analysis on fields, lists. All parameters of "cost" and "listSize" directives are supported, except for "sizedFields": * "cost" and "listSize" directives on fields of various types * variables passed as values to argumets * interfaces and unions, lists ## Notes Planning visitor collects information for the costCalculator via EnterField and LeaveField hooks. Calculator builds a tree of nodes, each node corresponding to the requested field. After the planning is done, a callee could get a ref to the calculator and request cost calculation. Cost calculation walks the previously built tree and using variables provided with operation, estimates the static cost. It builds on top of IBM spec for @cost and @listsize directive with a few changes: * It uses Int! for weights instead of stringified floats. * When weight is specified for the type and a field returns the list of that type, this weight (along with children's costs) is multiplied too. A few things on the TBD list: * Support of SizedFields of @listsize * Weights on fields of InputObjects with recursion * Weights on arguments of directives Fixes ENG-8732
1 parent 29798be commit 004f68e

File tree

14 files changed

+2244
-99
lines changed

14 files changed

+2244
-99
lines changed

execution/engine/execution_engine.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ func newInternalExecutionContext() *internalExecutionContext {
3838
}
3939
}
4040

41-
func (e *internalExecutionContext) prepare(ctx context.Context, variables []byte, request resolve.Request) {
42-
e.setContext(ctx)
43-
e.setVariables(variables)
44-
e.setRequest(request)
45-
}
46-
4741
func (e *internalExecutionContext) setRequest(request resolve.Request) {
4842
e.resolveContext.Request = request
4943
}
@@ -194,7 +188,10 @@ func (e *ExecutionEngine) Execute(ctx context.Context, operation *graphql.Reques
194188
}
195189

196190
execContext := newInternalExecutionContext()
197-
execContext.prepare(ctx, operation.Variables, operation.InternalRequest())
191+
execContext.setContext(ctx)
192+
execContext.setVariables(operation.Variables)
193+
execContext.setRequest(operation.InternalRequest())
194+
198195
for i := range options {
199196
options[i](execContext)
200197
}
@@ -210,10 +207,13 @@ func (e *ExecutionEngine) Execute(ctx context.Context, operation *graphql.Reques
210207
}
211208

212209
var report operationreport.Report
213-
cachedPlan := e.getCachedPlan(execContext, operation.Document(), e.config.schema.Document(), operation.OperationName, &report)
210+
cachedPlan, costCalculator := e.getCachedPlan(execContext, operation.Document(), e.config.schema.Document(), operation.OperationName, &report)
214211
if report.HasErrors() {
215212
return report
216213
}
214+
operation.ComputeStaticCost(costCalculator, e.config.plannerConfig, execContext.resolveContext.Variables)
215+
// Debugging of cost trees. Do not remove.
216+
// fmt.Println(costCalculator.DebugPrint(e.config.plannerConfig, execContext.resolveContext.Variables))
217217

218218
if execContext.resolveContext.TracingOptions.Enable && !execContext.resolveContext.TracingOptions.ExcludePlannerStats {
219219
planningTime := resolve.GetDurationNanoSinceTraceStart(execContext.resolveContext.Context()) - tracePlanStart
@@ -236,33 +236,33 @@ func (e *ExecutionEngine) Execute(ctx context.Context, operation *graphql.Reques
236236
}
237237
}
238238

239-
func (e *ExecutionEngine) getCachedPlan(ctx *internalExecutionContext, operation, definition *ast.Document, operationName string, report *operationreport.Report) plan.Plan {
239+
func (e *ExecutionEngine) getCachedPlan(ctx *internalExecutionContext, operation, definition *ast.Document, operationName string, report *operationreport.Report) (plan.Plan, *plan.CostCalculator) {
240240
hash := pool.Hash64.Get()
241241
hash.Reset()
242242
defer pool.Hash64.Put(hash)
243243
err := astprinter.Print(operation, hash)
244244
if err != nil {
245245
report.AddInternalError(err)
246-
return nil
246+
return nil, nil
247247
}
248248

249249
cacheKey := hash.Sum64()
250250

251251
if cached, ok := e.executionPlanCache.Get(cacheKey); ok {
252252
if p, ok := cached.(plan.Plan); ok {
253-
return p
253+
return p, p.GetStaticCostCalculator()
254254
}
255255
}
256256

257257
planner, _ := plan.NewPlanner(e.config.plannerConfig)
258258
planResult := planner.Plan(operation, definition, operationName, report)
259259
if report.HasErrors() {
260-
return nil
260+
return nil, nil
261261
}
262262

263263
ctx.postProcessor.Process(planResult)
264264
e.executionPlanCache.Add(cacheKey, planResult)
265-
return planResult
265+
return planResult, planResult.GetStaticCostCalculator()
266266
}
267267

268268
func (e *ExecutionEngine) GetWebsocketBeforeStartHook() WebsocketBeforeStartHook {

0 commit comments

Comments
 (0)