Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 10 additions & 20 deletions src/jmh/java/performance/DataLoaderPerformance.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

@State(Scope.Benchmark)
@Warmup(iterations = 2, time = 5)
@Measurement(iterations = 3)
@Measurement(iterations = 5)
@Fork(2)
public class DataLoaderPerformance {

Expand Down Expand Up @@ -481,25 +481,17 @@ public Pet(String id, String name, String ownerId, List<String> friendsIds) {


static BatchLoader<String, Owner> ownerBatchLoader = list -> {
// System.out.println("OwnerBatchLoader with " + list.size() );
List<Owner> collect = list.stream().map(key -> {
Owner owner = owners.get(key);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return owner;
}).collect(Collectors.toList());
return CompletableFuture.completedFuture(collect);
};
static BatchLoader<String, Pet> petBatchLoader = list -> {
// System.out.println("PetBatchLoader with list: " + list.size());
List<Pet> collect = list.stream().map(key -> {
Pet owner = pets.get(key);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return owner;
}).collect(Collectors.toList());
return CompletableFuture.completedFuture(collect);
Expand All @@ -520,9 +512,6 @@ public void setup() {
try {
String sdl = PerformanceTestingUtils.loadResource("dataLoaderPerformanceSchema.graphqls");


DataLoaderRegistry registry = new DataLoaderRegistry();

DataFetcher ownersDF = (env -> {
// Load all 103 owners (O-1 through O-103)
List<Object> allOwnerIds = List.of(
Expand All @@ -542,20 +531,20 @@ public void setup() {
});
DataFetcher petsDf = (env -> {
Owner owner = env.getSource();
return env.getDataLoader(petDLName).loadMany((List) owner.petIds);
// .thenCompose((result) -> CompletableFuture.supplyAsync(() -> null).thenApply((__) -> result));
return env.getDataLoader(petDLName).loadMany((List) owner.petIds)
.thenCompose((result) -> CompletableFuture.supplyAsync(() -> null).thenApply((__) -> result));
});

DataFetcher petFriendsDF = (env -> {
Pet pet = env.getSource();
return env.getDataLoader(petDLName).loadMany((List) pet.friendsIds);
// .thenCompose((result) -> CompletableFuture.supplyAsync(() -> null).thenApply((__) -> result));
return env.getDataLoader(petDLName).loadMany((List) pet.friendsIds)
.thenCompose((result) -> CompletableFuture.supplyAsync(() -> null).thenApply((__) -> result));
});

DataFetcher petOwnerDF = (env -> {
Pet pet = env.getSource();
return env.getDataLoader(ownerDLName).load(pet.ownerId);
// .thenCompose((result) -> CompletableFuture.supplyAsync(() -> null).thenApply((__) -> result));
return env.getDataLoader(ownerDLName).load(pet.ownerId)
.thenCompose((result) -> CompletableFuture.supplyAsync(() -> null).thenApply((__) -> result));
});


Expand Down Expand Up @@ -598,6 +587,7 @@ public void executeRequestWithDataLoaders(MyState myState, Blackhole blackhole)
// .profileExecution(true)
.build();
executionInput.getGraphQLContext().put(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING, true);
// executionInput.getGraphQLContext().put(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING, true);
ExecutionResult execute = myState.graphQL.execute(executionInput);
// ProfilerResult profilerResult = executionInput.getGraphQLContext().get(ProfilerResult.PROFILER_CONTEXT_KEY);
// System.out.println("execute: " + execute);
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/graphql/execution/Async.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static graphql.Assert.assertTrue;
import static java.util.stream.Collectors.toList;

@Internal
@SuppressWarnings("FutureReturnValueIgnored")
Expand Down Expand Up @@ -408,4 +411,24 @@ public static <T> CompletableFuture<T> exceptionallyCompletedFuture(Throwable ex
public static <T> @NonNull CompletableFuture<T> orNullCompletedFuture(@Nullable CompletableFuture<T> completableFuture) {
return completableFuture != null ? completableFuture : CompletableFuture.completedFuture(null);
}

public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> cfs) {
return CompletableFuture.allOf(cfs.toArray(CompletableFuture[]::new))
.thenApply(v -> cfs.stream()
.map(CompletableFuture::join)
.collect(toList())
);
}

public static <K, V> CompletableFuture<Map<K, V>> allOf(Map<K, CompletableFuture<V>> cfs) {
return CompletableFuture.allOf(cfs.values().toArray(CompletableFuture[]::new))
.thenApply(v -> cfs.entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
task -> task.getValue().join())
)
);
}

}
1 change: 1 addition & 0 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
.selectionSet(fieldCollector)
.queryDirectives(queryDirectives)
.deferredCallContext(parameters.getDeferredCallContext())
.level(parameters.getPath().getLevel())
.build();
});

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/graphql/execution/ResultPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public static ResultPath rootPath() {
private ResultPath() {
parent = null;
segment = null;
this.toStringValue = initString();
this.level = 0;
this.toStringValue = initString();
}

private ResultPath(ResultPath parent, String segment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@ private static class ChainedDLStack {
// a state for level points to a previous one
// all the invocations that are linked together are the relevant invocations for the next dispatch
private static class StateForLevel {
final @Nullable DataLoaderInvocation dataLoaderInvocation;
final @Nullable DataLoader dataLoader;
final boolean dispatchingStarted;
final boolean dispatchingFinished;
final boolean currentlyDelayedDispatching;
final @Nullable StateForLevel prev;

public StateForLevel(@Nullable DataLoaderInvocation dataLoaderInvocation,
public StateForLevel(@Nullable DataLoader dataLoader,
boolean dispatchingStarted,
boolean dispatchingFinished,
boolean currentlyDelayedDispatching,
@Nullable StateForLevel prev) {
this.dataLoaderInvocation = dataLoaderInvocation;
this.dataLoader = dataLoader;
this.dispatchingStarted = dispatchingStarted;
this.dispatchingFinished = dispatchingFinished;
this.currentlyDelayedDispatching = currentlyDelayedDispatching;
Expand Down Expand Up @@ -91,7 +91,7 @@ public StateForLevel(@Nullable DataLoaderInvocation dataLoaderInvocation,
}
}

if (currentState == null || currentState.dataLoaderInvocation == null) {
if (currentState == null || currentState.dataLoader == null) {
if (normalDispatchOrDelayed) {
dispatchingFinished = true;
} else {
Expand All @@ -108,8 +108,7 @@ public StateForLevel(@Nullable DataLoaderInvocation dataLoaderInvocation,
}


public boolean newDataLoaderInvocation(DataLoaderInvocation dataLoaderInvocation) {
int level = dataLoaderInvocation.level;
public boolean newDataLoaderInvocation(int level, DataLoader dataLoader) {
AtomicReference<@Nullable StateForLevel> currentStateRef = stateMapPerLevel.computeIfAbsent(level, __ -> new AtomicReference<>());
while (true) {
StateForLevel currentState = currentStateRef.get();
Expand All @@ -132,7 +131,7 @@ public boolean newDataLoaderInvocation(DataLoaderInvocation dataLoaderInvocation
currentlyDelayedDispatching = true;
}

StateForLevel newState = new StateForLevel(dataLoaderInvocation, dispatchingStarted, dispatchingFinished, currentlyDelayedDispatching, currentState);
StateForLevel newState = new StateForLevel(dataLoader, dispatchingStarted, dispatchingFinished, currentlyDelayedDispatching, currentState);

if (currentStateRef.compareAndSet(currentState, newState)) {
return newDelayedInvocation;
Expand Down Expand Up @@ -487,20 +486,14 @@ private void dispatchAll(DataLoaderRegistry dataLoaderRegistry, int level) {
private void dispatchDLCFImpl(Integer level, CallStack callStack, boolean normalOrDelayed, boolean chained) {

ChainedDLStack.StateForLevel stateForLevel = callStack.chainedDLStack.aboutToStartDispatching(level, normalOrDelayed, chained);
if (stateForLevel == null || stateForLevel.dataLoaderInvocation == null) {
if (stateForLevel == null || stateForLevel.dataLoader == null) {
return;
}

List<CompletableFuture> allDispatchedCFs = new ArrayList<>();
while (stateForLevel != null && stateForLevel.dataLoaderInvocation != null) {
final DataLoaderInvocation invocation = stateForLevel.dataLoaderInvocation;
CompletableFuture<List> dispatch = invocation.dataLoader.dispatch();
while (stateForLevel != null && stateForLevel.dataLoader != null) {
CompletableFuture<List> dispatch = stateForLevel.dataLoader.dispatch();
allDispatchedCFs.add(dispatch);
dispatch.whenComplete((objects, throwable) -> {
if (objects != null && objects.size() > 0) {
profiler.batchLoadedNewStrategy(invocation.name, level, objects.size(), !normalOrDelayed, chained);
}
});
stateForLevel = stateForLevel.prev;
}
CompletableFuture.allOf(allDispatchedCFs.toArray(new CompletableFuture[0]))
Expand All @@ -512,51 +505,19 @@ private void dispatchDLCFImpl(Integer level, CallStack callStack, boolean normal
}


public void newDataLoaderInvocation(String resultPath,
int level,
public void newDataLoaderInvocation(int level,
DataLoader dataLoader,
String dataLoaderName,
Object key,
@Nullable AlternativeCallContext alternativeCallContext) {
if (!enableDataLoaderChaining) {
return;
}
DataLoaderInvocation dataLoaderInvocation = new DataLoaderInvocation(resultPath, level, dataLoader, dataLoaderName, key);
CallStack callStack = getCallStack(alternativeCallContext);
boolean newDelayedInvocation = callStack.chainedDLStack.newDataLoaderInvocation(dataLoaderInvocation);
boolean newDelayedInvocation = callStack.chainedDLStack.newDataLoaderInvocation(level, dataLoader);
if (newDelayedInvocation) {
dispatchDLCFImpl(level, callStack, false, false);
}
}

/**
* A single data loader invocation.
*/
private static class DataLoaderInvocation {
final String resultPath;
final int level;
final DataLoader dataLoader;
final String name;
final Object key;

public DataLoaderInvocation(String resultPath, int level, DataLoader dataLoader, String name, Object key) {
this.resultPath = resultPath;
this.level = level;
this.dataLoader = dataLoader;
this.name = name;
this.key = key;
}

@Override
public String toString() {
return "ResultPathWithDataLoader{" +
"resultPath='" + resultPath + '\'' +
", level=" + level +
", key=" + key +
", name='" + name + '\'' +
'}';
}
}

}

13 changes: 13 additions & 0 deletions src/main/java/graphql/schema/DataFetchingEnvironmentImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class DataFetchingEnvironmentImpl implements DataFetchingEnvironment {
private final Document document;
private final ImmutableMapWithNullValues<String, Object> variables;
private final QueryDirectives queryDirectives;
private final int level;

// used for internal() method
private final DFEInternalState dfeInternalState;
Expand All @@ -86,6 +87,7 @@ private DataFetchingEnvironmentImpl(Builder builder) {
this.document = builder.document;
this.variables = builder.variables == null ? ImmutableMapWithNullValues.emptyMap() : builder.variables;
this.queryDirectives = builder.queryDirectives;
this.level = builder.level;

// internal state
this.dfeInternalState = new DFEInternalState(builder.dataLoaderDispatchStrategy, builder.alternativeCallContext, builder.profiler);
Expand Down Expand Up @@ -278,6 +280,10 @@ public String toString() {
'}';
}

public int getLevel() {
return level;
}

@NullUnmarked
public static class Builder {

Expand Down Expand Up @@ -305,6 +311,7 @@ public static class Builder {
private DataLoaderDispatchStrategy dataLoaderDispatchStrategy;
private Profiler profiler;
private AlternativeCallContext alternativeCallContext;
private int level;

public Builder(DataFetchingEnvironmentImpl env) {
this.source = env.source;
Expand All @@ -331,6 +338,7 @@ public Builder(DataFetchingEnvironmentImpl env) {
this.dataLoaderDispatchStrategy = env.dfeInternalState.dataLoaderDispatchStrategy;
this.profiler = env.dfeInternalState.profiler;
this.alternativeCallContext = env.dfeInternalState.alternativeCallContext;
this.level = env.level;
}

public Builder() {
Expand Down Expand Up @@ -468,6 +476,11 @@ public Builder profiler(Profiler profiler) {
this.profiler = profiler;
return this;
}

public Builder level(int level) {
this.level = level;
return this;
}
}

@Internal
Expand Down
Loading