@@ -20,35 +20,48 @@ public static async Task<IExecutionResult> ExecuteAsync(
2020 OperationPlan operationPlan ,
2121 CancellationToken cancellationToken )
2222 {
23- // We create a new CancellationTokenSource that can be used to halt the execution engine,
24- // without also cancelling the entire request pipeline.
25- using var executionCts = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
26-
2723 await using var context = requestContext . Schema . Services . GetRequiredService < OperationPlanContextPool > ( ) . Rent ( ) ;
28- context . Initialize ( requestContext , variables , operationPlan , executionCts ) ;
2924
30- context . Begin ( ) ;
25+ // We reuse a pooled CancellationTokenSource that can halt the execution engine without
26+ // cancelling the entire request pipeline. The request token is linked in so that
27+ // client-abort / server-shutdown still propagates.
28+ var ( executionCts , cancellationRegistration ) = context . RentEngineCancellation ( cancellationToken ) ;
3129
32- switch ( operationPlan . Operation . Definition . Operation )
30+ try
3331 {
34- case OperationType . Query :
35- await ExecuteQueryAsync ( context , operationPlan , executionCts . Token ) ;
36- break ;
32+ context . Initialize ( requestContext , variables , operationPlan , executionCts ) ;
3733
38- case OperationType . Mutation :
39- await ExecuteMutationAsync ( context , operationPlan , executionCts . Token ) ;
40- break ;
34+ context . Begin ( ) ;
4135
42- default :
43- throw new InvalidOperationException ( "Only queries and mutations can be executed." ) ;
44- }
36+ switch ( operationPlan . Operation . Definition . Operation )
37+ {
38+ case OperationType . Query :
39+ await ExecuteQueryAsync ( context , operationPlan , executionCts . Token ) ;
40+ break ;
4541
46- // If the original CancellationToken of the request was cancelled,
47- // the Execution nodes and the PlanExecutor should have been gracefully cancelled,
48- // so we throw here to properly cancel the request execution.
49- cancellationToken . ThrowIfCancellationRequested ( ) ;
42+ case OperationType . Mutation :
43+ await ExecuteMutationAsync ( context , operationPlan , executionCts . Token ) ;
44+ break ;
45+
46+ default :
47+ throw new InvalidOperationException ( "Only queries and mutations can be executed." ) ;
48+ }
5049
51- return context . Complete ( ) ;
50+ // If the original CancellationToken of the request was cancelled,
51+ // the Execution nodes and the PlanExecutor should have been gracefully cancelled,
52+ // so we throw here to properly cancel the request execution.
53+ cancellationToken . ThrowIfCancellationRequested ( ) ;
54+
55+ return context . Complete ( ) ;
56+ }
57+ finally
58+ {
59+ // Dispose the parent-token registration BEFORE returning the source for reuse so a
60+ // stale registration can never fire into a reset source. DisposeAsync waits for an
61+ // in-flight cancel callback to finish.
62+ await cancellationRegistration . DisposeAsync ( ) ;
63+ context . ReturnEngineCancellation ( ) ;
64+ }
5265 }
5366
5467 public static async Task < IExecutionResult > ExecuteWithDeferAsync (
0 commit comments