|
7 | 7 | import graphql.ExecutionResultImpl; |
8 | 8 | import graphql.Internal; |
9 | 9 | import graphql.execution.ExecutionContext; |
| 10 | +import graphql.execution.ExecutionStepInfo; |
10 | 11 | import graphql.execution.ExecutionStrategyParameters; |
11 | 12 | import graphql.execution.FieldValueInfo; |
12 | 13 | import graphql.execution.MergedField; |
13 | 14 | import graphql.execution.MergedSelectionSet; |
14 | 15 | import graphql.execution.ResultPath; |
15 | 16 | import graphql.execution.instrumentation.Instrumentation; |
| 17 | +import graphql.execution.instrumentation.InstrumentationContext; |
| 18 | +import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters; |
16 | 19 | import graphql.incremental.IncrementalPayload; |
17 | 20 | import graphql.util.FpKit; |
| 21 | +import org.jspecify.annotations.NonNull; |
18 | 22 |
|
19 | 23 | import java.util.Collections; |
20 | 24 | import java.util.HashMap; |
|
27 | 31 | import java.util.function.BiFunction; |
28 | 32 | import java.util.function.Supplier; |
29 | 33 |
|
| 34 | +import static graphql.execution.instrumentation.SimpleInstrumentationContext.nonNullCtx; |
| 35 | + |
30 | 36 | /** |
31 | 37 | * The purpose of this class hierarchy is to encapsulate most of the logic for deferring field execution, thus |
32 | 38 | * keeping the main execution strategy code clean and focused on the main execution logic. |
@@ -59,16 +65,19 @@ class DeferredExecutionSupportImpl implements DeferredExecutionSupport { |
59 | 65 | private final ExecutionStrategyParameters parameters; |
60 | 66 | private final ExecutionContext executionContext; |
61 | 67 | private final BiFunction<ExecutionContext, ExecutionStrategyParameters, CompletableFuture<FieldValueInfo>> resolveFieldWithInfoFn; |
| 68 | + private final BiFunction<ExecutionContext, ExecutionStrategyParameters, Supplier<ExecutionStepInfo>> executionStepInfoFn; |
62 | 69 | private final Map<String, Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult>>> dfCache = new HashMap<>(); |
63 | 70 |
|
64 | 71 | public DeferredExecutionSupportImpl( |
65 | 72 | MergedSelectionSet mergedSelectionSet, |
66 | 73 | ExecutionStrategyParameters parameters, |
67 | 74 | ExecutionContext executionContext, |
68 | | - BiFunction<ExecutionContext, ExecutionStrategyParameters, CompletableFuture<FieldValueInfo>> resolveFieldWithInfoFn |
| 75 | + BiFunction<ExecutionContext, ExecutionStrategyParameters, CompletableFuture<FieldValueInfo>> resolveFieldWithInfoFn, |
| 76 | + BiFunction<ExecutionContext, ExecutionStrategyParameters, Supplier<ExecutionStepInfo>> executionStepInfoFn |
69 | 77 | ) { |
70 | 78 | this.executionContext = executionContext; |
71 | 79 | this.resolveFieldWithInfoFn = resolveFieldWithInfoFn; |
| 80 | + this.executionStepInfoFn = executionStepInfoFn; |
72 | 81 | ImmutableListMultimap.Builder<DeferredExecution, MergedField> deferredExecutionToFieldsBuilder = ImmutableListMultimap.builder(); |
73 | 82 | ImmutableSet.Builder<MergedField> deferredFieldsBuilder = ImmutableSet.builder(); |
74 | 83 | ImmutableList.Builder<String> nonDeferredFieldNamesBuilder = ImmutableList.builder(); |
@@ -153,37 +162,46 @@ private Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult |
153 | 162 | } |
154 | 163 | ); |
155 | 164 |
|
156 | | - |
157 | | - Instrumentation instrumentation = executionContext.getInstrumentation(); |
158 | | - |
159 | | - instrumentation.beginDeferredField(executionContext.getInstrumentationState()); |
160 | | - |
161 | 165 | // todo: handle cached computations |
162 | 166 | return dfCache.computeIfAbsent( |
163 | 167 | currentField.getResultKey(), |
164 | 168 | // The same field can be associated with multiple defer executions, so |
165 | 169 | // we memoize the field resolution to avoid multiple calls to the same data fetcher |
166 | | - key -> FpKit.interThreadMemoize(() -> { |
167 | | - CompletableFuture<FieldValueInfo> fieldValueResult = resolveFieldWithInfoFn.apply(executionContext, executionStrategyParameters); |
| 170 | + key -> FpKit.interThreadMemoize(resolveDeferredFieldValue(currentField, executionContext, executionStrategyParameters) |
| 171 | + ) |
| 172 | + ); |
| 173 | + } |
168 | 174 |
|
169 | | - fieldValueResult.whenComplete((fieldValueInfo, throwable) -> { |
170 | | - executionContext.getDataLoaderDispatcherStrategy().deferredOnFieldValue(currentField.getResultKey(), fieldValueInfo, throwable, executionStrategyParameters); |
171 | | - }); |
| 175 | + @NonNull |
| 176 | + private Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult>> resolveDeferredFieldValue(MergedField currentField, ExecutionContext executionContext, ExecutionStrategyParameters executionStrategyParameters) { |
| 177 | + return () -> { |
172 | 178 |
|
| 179 | + Instrumentation instrumentation = executionContext.getInstrumentation(); |
| 180 | + Supplier<ExecutionStepInfo> executionStepInfo = executionStepInfoFn.apply(executionContext, executionStrategyParameters); |
| 181 | + InstrumentationFieldParameters fieldParameters = new InstrumentationFieldParameters(executionContext, executionStepInfo); |
| 182 | + InstrumentationContext<Object> deferredFieldCtx = nonNullCtx(instrumentation.beginDeferredField(fieldParameters, executionContext.getInstrumentationState())); |
173 | 183 |
|
174 | | - CompletableFuture<ExecutionResult> executionResultCF = fieldValueResult |
175 | | - .thenCompose(fvi -> fvi |
176 | | - .getFieldValueFuture() |
177 | | - .thenApply(fv -> ExecutionResultImpl.newExecutionResult().data(fv).build()) |
178 | | - ); |
| 184 | + CompletableFuture<FieldValueInfo> fieldValueResult = resolveFieldWithInfoFn.apply(this.executionContext, executionStrategyParameters); |
179 | 185 |
|
180 | | - return executionResultCF |
181 | | - .thenApply(executionResult -> |
182 | | - new DeferredFragmentCall.FieldWithExecutionResult(currentField.getResultKey(), executionResult) |
183 | | - ); |
184 | | - } |
185 | | - ) |
186 | | - ); |
| 186 | + deferredFieldCtx.onDispatched(); |
| 187 | + |
| 188 | + fieldValueResult.whenComplete((fieldValueInfo, throwable) -> { |
| 189 | + this.executionContext.getDataLoaderDispatcherStrategy().deferredOnFieldValue(currentField.getResultKey(), fieldValueInfo, throwable, executionStrategyParameters); |
| 190 | + deferredFieldCtx.onCompleted(fieldValueInfo, throwable); |
| 191 | + }); |
| 192 | + |
| 193 | + |
| 194 | + CompletableFuture<ExecutionResult> executionResultCF = fieldValueResult |
| 195 | + .thenCompose(fvi -> fvi |
| 196 | + .getFieldValueFuture() |
| 197 | + .thenApply(fv -> ExecutionResultImpl.newExecutionResult().data(fv).build()) |
| 198 | + ); |
| 199 | + |
| 200 | + return executionResultCF |
| 201 | + .thenApply(executionResult -> |
| 202 | + new DeferredFragmentCall.FieldWithExecutionResult(currentField.getResultKey(), executionResult) |
| 203 | + ); |
| 204 | + }; |
187 | 205 | } |
188 | 206 | } |
189 | 207 |
|
|
0 commit comments