Skip to content
8 changes: 5 additions & 3 deletions src/main/java/graphql/execution/AsyncExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public AsyncExecutionStrategy(DataFetcherExceptionHandler exceptionHandler) {
@SuppressWarnings("FutureReturnValueIgnored")
public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
DataLoaderDispatchStrategy dataLoaderDispatcherStrategy = executionContext.getDataLoaderDispatcherStrategy();
dataLoaderDispatcherStrategy.executionStrategy(executionContext, parameters);
Instrumentation instrumentation = executionContext.getInstrumentation();
InstrumentationExecutionStrategyParameters instrumentationParameters = new InstrumentationExecutionStrategyParameters(executionContext, parameters);

Expand All @@ -54,6 +53,9 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont
}

DeferredExecutionSupport deferredExecutionSupport = createDeferredExecutionSupport(executionContext, parameters);

dataLoaderDispatcherStrategy.executionStrategy(executionContext, parameters, deferredExecutionSupport.getNonDeferredFieldNames(fieldNames).size());

Async.CombinedBuilder<FieldValueInfo> futures = getAsyncFieldValueInfo(executionContext, parameters, deferredExecutionSupport);

CompletableFuture<ExecutionResult> overallResult = new CompletableFuture<>();
Expand All @@ -72,14 +74,14 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont
for (FieldValueInfo completeValueInfo : completeValueInfos) {
fieldValuesFutures.addObject(completeValueInfo.getFieldValueObject());
}
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(completeValueInfos);
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(completeValueInfos, parameters);
executionStrategyCtx.onFieldValuesInfo(completeValueInfos);
fieldValuesFutures.await().whenComplete(handleResultsConsumer);
}).exceptionally((ex) -> {
// if there are any issues with combining/handling the field results,
// complete the future at all costs and bubble up any thrown exception so
// the execution does not hang.
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesException(ex);
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesException(ex, parameters);
executionStrategyCtx.onFieldValuesException();
overallResult.completeExceptionally(ex);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ private Object resolveSerialField(ExecutionContext executionContext,
if (fieldWithInfo instanceof CompletableFuture) {
//noinspection unchecked
return ((CompletableFuture<FieldValueInfo>) fieldWithInfo).thenCompose(fvi -> {
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(List.of(fvi));
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(List.of(fvi), newParameters);
CompletableFuture<Object> fieldValueFuture = fvi.getFieldValueFuture();
return fieldValueFuture;
});
} else {
FieldValueInfo fvi = (FieldValueInfo) fieldWithInfo;
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(List.of(fvi));
dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(List.of(fvi), newParameters);
return fvi.getFieldValueObject();
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/graphql/execution/DataLoaderDispatchStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,35 @@ public interface DataLoaderDispatchStrategy {
};


default void executionStrategy(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
default void executionStrategy(ExecutionContext executionContext, ExecutionStrategyParameters parameters, int fieldCount) {

}

default void executionSerialStrategy(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {

}

default void executionStrategyOnFieldValuesInfo(List<FieldValueInfo> fieldValueInfoList) {
default void executionStrategyOnFieldValuesInfo(List<FieldValueInfo> fieldValueInfoList, ExecutionStrategyParameters parameters) {

}

default void executionStrategyOnFieldValuesException(Throwable t) {
default void executionStrategyOnFieldValuesException(Throwable t, ExecutionStrategyParameters parameters) {

}


default void executeObject(ExecutionContext executionContext, ExecutionStrategyParameters executionStrategyParameters) {
default void executeObject(ExecutionContext executionContext, ExecutionStrategyParameters executionStrategyParameters, int fieldCount) {

}

default void executeObjectOnFieldValuesInfo(List<FieldValueInfo> fieldValueInfoList, ExecutionStrategyParameters parameters) {

}

default void deferredOnFieldValue(String resultKey, FieldValueInfo fieldValueInfo, Throwable throwable, ExecutionStrategyParameters parameters) {

}

default void executeObjectOnFieldValuesException(Throwable t, ExecutionStrategyParameters parameters) {

}
Expand All @@ -55,8 +59,4 @@ default void fieldFetched(ExecutionContext executionContext,
default DataFetcher<?> modifyDataFetcher(DataFetcher<?> dataFetcher) {
return dataFetcher;
}

default void executeDeferredOnFieldValueInfo(FieldValueInfo fieldValueInfo, ExecutionStrategyParameters executionStrategyParameters) {

}
}
10 changes: 1 addition & 9 deletions src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.ExecutionResultImpl;
import graphql.ExperimentalApi;
import graphql.GraphQLContext;
import graphql.GraphQLError;
import graphql.Internal;
Expand All @@ -16,7 +15,6 @@
import graphql.execution.instrumentation.InstrumentationState;
import graphql.execution.instrumentation.dataloader.FallbackDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch;
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.extensions.ExtensionsBuilder;
Expand All @@ -37,7 +35,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

Expand Down Expand Up @@ -258,12 +255,7 @@ private DataLoaderDispatchStrategy createDataLoaderDispatchStrategy(ExecutionCon
return DataLoaderDispatchStrategy.NO_OP;
}
if (!executionContext.isSubscriptionOperation()) {
boolean deferEnabled = executionContext.hasIncrementalSupport();

// Dedicated strategy for defer support, for safety purposes.
return deferEnabled ?
new PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch(executionContext) :
new PerLevelDataLoaderDispatchStrategy(executionContext);
return new PerLevelDataLoaderDispatchStrategy(executionContext);
} else {
return new FallbackDataLoaderDispatchStrategy(executionContext);
}
Expand Down
8 changes: 3 additions & 5 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import graphql.EngineRunningState;
import graphql.ExecutionResult;
import graphql.ExecutionResultImpl;
import graphql.ExperimentalApi;
import graphql.GraphQLError;
import graphql.Internal;
import graphql.PublicSpi;
Expand Down Expand Up @@ -50,7 +49,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
Expand Down Expand Up @@ -197,7 +195,6 @@ public static String mkNameForPath(List<Field> currentField) {
@DuckTyped(shape = "CompletableFuture<Map<String, Object>> | Map<String, Object>")
protected Object executeObject(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
DataLoaderDispatchStrategy dataLoaderDispatcherStrategy = executionContext.getDataLoaderDispatcherStrategy();
dataLoaderDispatcherStrategy.executeObject(executionContext, parameters);
Instrumentation instrumentation = executionContext.getInstrumentation();
InstrumentationExecutionStrategyParameters instrumentationParameters = new InstrumentationExecutionStrategyParameters(executionContext, parameters);

Expand All @@ -212,6 +209,7 @@ protected Object executeObject(ExecutionContext executionContext, ExecutionStrat

CompletableFuture<Map<String, Object>> overallResult = new CompletableFuture<>();
List<String> fieldsExecutedOnInitialResult = deferredExecutionSupport.getNonDeferredFieldNames(fieldNames);
dataLoaderDispatcherStrategy.executeObject(executionContext, parameters, fieldsExecutedOnInitialResult.size());
BiConsumer<List<Object>, Throwable> handleResultsConsumer = buildFieldValueMap(fieldsExecutedOnInitialResult, overallResult, executionContext);

resolveObjectCtx.onDispatched();
Expand Down Expand Up @@ -300,7 +298,7 @@ DeferredExecutionSupport createDeferredExecutionSupport(ExecutionContext executi
) {
MergedSelectionSet fields = parameters.getFields();

executionContext.getIncrementalCallState().enqueue(deferredExecutionSupport.createCalls(parameters));
executionContext.getIncrementalCallState().enqueue(deferredExecutionSupport.createCalls());

// Only non-deferred fields should be considered for calculating the expected size of futures.
Async.CombinedBuilder<FieldValueInfo> futures = Async
Expand Down Expand Up @@ -400,7 +398,6 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
}

MergedField field = parameters.getField();
String pathString = parameters.getPath().toString();
GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType();

// if the DF (like PropertyDataFetcher) does not use the arguments or execution step info then dont build any
Expand Down Expand Up @@ -435,6 +432,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
.parentType(parentType)
.selectionSet(fieldCollector)
.queryDirectives(queryDirectives)
.deferredCallContext(parameters.getDeferredCallContext())
.build();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public ExecutionStrategyParameters getParent() {
* @return the deferred call context or null if we're not in the scope of a deferred call
*/
@Nullable
@Internal
public DeferredCallContext getDeferredCallContext() {
return deferredCallContext;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import graphql.GraphQLError;
import graphql.Internal;
import graphql.VisibleForTesting;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
Expand All @@ -18,8 +19,31 @@
@Internal
public class DeferredCallContext {

private final int startLevel;
private final int fields;

private final List<GraphQLError> errors = new CopyOnWriteArrayList<>();

public DeferredCallContext(int startLevel, int fields) {
this.startLevel = startLevel;
this.fields = fields;
}

@VisibleForTesting
public DeferredCallContext() {
this.startLevel = 0;
this.fields = 0;
}

public int getStartLevel() {
return startLevel;
}

public int getFields() {
return fields;
}


public void addErrors(List<GraphQLError> errors) {
this.errors.addAll(errors);
}
Expand All @@ -34,4 +58,6 @@ public void addError(GraphQLError graphqlError) {
public List<GraphQLError> getErrors() {
return errors;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public interface DeferredExecutionSupport {

List<String> getNonDeferredFieldNames(List<String> allFieldNames);

Set<IncrementalCall<? extends IncrementalPayload>> createCalls(ExecutionStrategyParameters executionStrategyParameters);
Set<IncrementalCall<? extends IncrementalPayload>> createCalls();

DeferredExecutionSupport NOOP = new DeferredExecutionSupport.NoOp();

Expand Down Expand Up @@ -106,23 +106,24 @@ public List<String> getNonDeferredFieldNames(List<String> allFieldNames) {
}

@Override
public Set<IncrementalCall<? extends IncrementalPayload>> createCalls(ExecutionStrategyParameters executionStrategyParameters) {
public Set<IncrementalCall<? extends IncrementalPayload>> createCalls() {
ImmutableSet<DeferredExecution> deferredExecutions = deferredExecutionToFields.keySet();
Set<IncrementalCall<? extends IncrementalPayload>> set = new HashSet<>(deferredExecutions.size());
for (DeferredExecution deferredExecution : deferredExecutions) {
set.add(this.createDeferredFragmentCall(deferredExecution, executionStrategyParameters));
set.add(this.createDeferredFragmentCall(deferredExecution));
}
return set;
}

private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferredExecution, ExecutionStrategyParameters executionStrategyParameters) {
DeferredCallContext deferredCallContext = new DeferredCallContext();
private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferredExecution) {
int level = parameters.getPath().getLevel() + 1;
DeferredCallContext deferredCallContext = new DeferredCallContext(level, deferredFields.size());

List<MergedField> mergedFields = deferredExecutionToFields.get(deferredExecution);

List<Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult>>> calls = FpKit.arrayListSizedTo(mergedFields);
for (MergedField currentField : mergedFields) {
calls.add(this.createResultSupplier(currentField, deferredCallContext, executionStrategyParameters));
calls.add(this.createResultSupplier(currentField, deferredCallContext));
}

return new DeferredFragmentCall(
Expand All @@ -135,13 +136,12 @@ private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferr

private Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult>> createResultSupplier(
MergedField currentField,
DeferredCallContext deferredCallContext,
ExecutionStrategyParameters executionStrategyParameters
DeferredCallContext deferredCallContext
) {
Map<String, MergedField> fields = new LinkedHashMap<>();
fields.put(currentField.getResultKey(), currentField);

ExecutionStrategyParameters callParameters = parameters.transform(builder ->
ExecutionStrategyParameters executionStrategyParameters = parameters.transform(builder ->
{
MergedSelectionSet mergedSelectionSet = MergedSelectionSet.newMergedSelectionSet().subFields(fields).build();
ResultPath path = parameters.getPath().segment(currentField.getResultKey());
Expand All @@ -158,22 +158,23 @@ private Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult

instrumentation.beginDeferredField(executionContext.getInstrumentationState());

// todo: handle cached computations
return dfCache.computeIfAbsent(
currentField.getResultKey(),
// The same field can be associated with multiple defer executions, so
// we memoize the field resolution to avoid multiple calls to the same data fetcher
key -> FpKit.interThreadMemoize(() -> {
CompletableFuture<FieldValueInfo> fieldValueResult = resolveFieldWithInfoFn
.apply(executionContext, callParameters);
CompletableFuture<FieldValueInfo> fieldValueResult = resolveFieldWithInfoFn.apply(executionContext, executionStrategyParameters);

fieldValueResult.whenComplete((fieldValueInfo, throwable) -> {
executionContext.getDataLoaderDispatcherStrategy().deferredOnFieldValue(currentField.getResultKey(), fieldValueInfo, throwable, executionStrategyParameters);
});

CompletableFuture<ExecutionResult> executionResultCF = fieldValueResult
.thenCompose(fvi -> {
executionContext.getDataLoaderDispatcherStrategy().executeDeferredOnFieldValueInfo(fvi, executionStrategyParameters);

return fvi
.getFieldValueFuture()
.thenApply(fv -> ExecutionResultImpl.newExecutionResult().data(fv).build());
}
CompletableFuture<ExecutionResult> executionResultCF = fieldValueResult
.thenCompose(fvi -> fvi
.getFieldValueFuture()
.thenApply(fv -> ExecutionResultImpl.newExecutionResult().data(fv).build())
);

return executionResultCF
Expand Down Expand Up @@ -207,7 +208,7 @@ public List<String> getNonDeferredFieldNames(List<String> allFieldNames) {
}

@Override
public Set<IncrementalCall<? extends IncrementalPayload>> createCalls(ExecutionStrategyParameters executionStrategyParameters) {
public Set<IncrementalCall<? extends IncrementalPayload>> createCalls() {
return Collections.emptySet();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,5 @@ private Supplier<SingleSubscriberPublisher<DelayedIncrementalPartialResult>> cre
public Publisher<DelayedIncrementalPartialResult> startDeferredCalls() {
return publisher.get();
}

}
Loading