Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
cleanup
  • Loading branch information
andimarek committed Sep 27, 2025
commit 2e5c616dd229e78c45d426b4c4e3223611f27ea4
22 changes: 16 additions & 6 deletions src/jmh/java/performance/DataLoaderPerformance.java
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,23 @@ public Pet(String id, String name, String ownerId, List<String> friendsIds) {
static BatchLoader<String, Owner> ownerBatchLoader = list -> {
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 -> {
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 Down Expand Up @@ -532,20 +542,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
18 changes: 17 additions & 1 deletion src/main/java/graphql/GraphQLUnusualConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import static graphql.Assert.assertNotNull;
import static graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING;
import static graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING;

/**
* This allows you to control "unusual" aspects of the GraphQL system
Expand Down Expand Up @@ -344,12 +345,16 @@ private DataloaderConfig(GraphQLContextConfiguration contextConfig) {
}

/**
* @return true if @defer and @stream behaviour is enabled for this execution.
* returns true if chained data loader dispatching is enabled
*/
public boolean isDataLoaderChainingEnabled() {
return contextConfig.getBoolean(ENABLE_DATA_LOADER_CHAINING);
}

public boolean isDataLoaderExhaustedDispatchingEnabled() {
return contextConfig.get(ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING);
}

/**
* Enables the ability that chained DataLoaders are dispatched automatically.
*/
Expand All @@ -359,6 +364,17 @@ public DataloaderConfig enableDataLoaderChaining(boolean enable) {
return this;
}

/**
* Enables a dispatching strategy that will dispatch as long as there is no
* other data fetcher or batch loader running.
*/
@ExperimentalApi
public DataloaderConfig enableDataLoaderExhaustedDispatching(boolean enable) {
contextConfig.put(ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING, enable);
return this;
}


}

public static class ResponseMapFactoryConfig extends BaseContextConfig {
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.InstrumentationState;
import graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys;
import graphql.execution.instrumentation.dataloader.ExhaustedDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.extensions.ExtensionsBuilder;
Expand Down Expand Up @@ -262,8 +264,10 @@ private DataLoaderDispatchStrategy createDataLoaderDispatchStrategy(ExecutionCon
if (executionContext.getDataLoaderRegistry() == EMPTY_DATALOADER_REGISTRY || doNotAutomaticallyDispatchDataLoader) {
return DataLoaderDispatchStrategy.NO_OP;
}
// return new PerLevelDataLoaderDispatchStrategy(executionContext);
return new ExhaustedDataLoaderDispatchStrategy(executionContext);
if (executionContext.getGraphQLContext().getBoolean(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING, false)) {
return new ExhaustedDataLoaderDispatchStrategy(executionContext);
}
return new PerLevelDataLoaderDispatchStrategy(executionContext);
}


Expand All @@ -274,7 +278,8 @@ private void addExtensionsBuilderNotPresent(GraphQLContext graphQLContext) {
}
}

private ExecutionResult mergeExtensionsBuilderIfPresent(ExecutionResult executionResult, GraphQLContext graphQLContext) {
private ExecutionResult mergeExtensionsBuilderIfPresent(ExecutionResult executionResult, GraphQLContext
graphQLContext) {
Object builder = graphQLContext.get(ExtensionsBuilder.class);
if (builder instanceof ExtensionsBuilder) {
ExtensionsBuilder extensionsBuilder = (ExtensionsBuilder) builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ private DataLoaderDispatchingContextKeys() {
public static final String ENABLE_DATA_LOADER_CHAINING = "__GJ_enable_data_loader_chaining";


/**
* Enabled a different dispatching strategy that mimics the JS event loop based one:
* DataLoader will be dispatched as soon as there is no data fetcher or batch loader currently running.
*
*/
public static final String ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING = "__GJ_enable_data_loader_exhausted_dispatching";

/**
* Enables the ability that chained DataLoaders are dispatched automatically.
*
Expand All @@ -34,5 +41,14 @@ public static void setEnableDataLoaderChaining(GraphQLContext graphQLContext, bo
graphQLContext.put(ENABLE_DATA_LOADER_CHAINING, enabled);
}

/**
* Enables the ability that chained DataLoaders are dispatched automatically.
*
* @param graphQLContext
*/
public static void setEnableDataLoaderExhaustedDispatching(GraphQLContext graphQLContext, boolean enabled) {
graphQLContext.put(ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING, enabled);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,7 @@ private void dispatchImpl(CallStack callStack) {
}


public void newDataLoaderInvocation(String resultPath,
int level,
DataLoader dataLoader,
String dataLoaderName,
Object key,
@Nullable AlternativeCallContext alternativeCallContext) {
// DataLoaderInvocation dataLoaderInvocation = new DataLoaderInvocation(resultPath, level, dataLoader, dataLoaderName, key);
public void newDataLoaderInvocation(@Nullable AlternativeCallContext alternativeCallContext) {
CallStack callStack = getCallStack(alternativeCallContext);
newDataLoaderInvocationMaybeDispatch(callStack);
}
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/graphql/schema/DataFetchingEnvironmentImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import graphql.execution.MergedField;
import graphql.execution.directives.QueryDirectives;
import graphql.execution.incremental.AlternativeCallContext;
import graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys;
import graphql.language.Document;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
Expand Down Expand Up @@ -232,9 +233,10 @@ public ExecutionStepInfo getExecutionStepInfo() {
if (dataLoader == null) {
return null;
}
// if (!graphQLContext.getBoolean(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING, false)) {
// return dataLoader;
// }
if (!graphQLContext.getBoolean(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING, false)
&& !graphQLContext.getBoolean(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING, false)) {
return dataLoader;
}
return new DataLoaderWithContext<>(this, dataLoaderName, dataLoader);
}

Expand Down Expand Up @@ -272,8 +274,8 @@ public Object toInternal() {
@Override
public String toString() {
return "DataFetchingEnvironmentImpl{" +
"executionStepInfo=" + executionStepInfo +
'}';
"executionStepInfo=" + executionStepInfo +
'}';
}

@NullUnmarked
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/graphql/schema/DataLoaderWithContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import graphql.Internal;
import graphql.execution.incremental.AlternativeCallContext;
import graphql.execution.instrumentation.dataloader.ExhaustedDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategy;
import org.dataloader.DataLoader;
import org.dataloader.DelegatingDataLoader;
import org.jspecify.annotations.NonNull;
Expand Down Expand Up @@ -32,11 +33,14 @@ public CompletableFuture<V> load(@NonNull K key, @Nullable Object keyContext) {
DataFetchingEnvironmentImpl dfeImpl = (DataFetchingEnvironmentImpl) dfe;
DataFetchingEnvironmentImpl.DFEInternalState dfeInternalState = (DataFetchingEnvironmentImpl.DFEInternalState) dfeImpl.toInternal();
dfeInternalState.getProfiler().dataLoaderUsed(dataLoaderName);
if (dfeInternalState.getDataLoaderDispatchStrategy() instanceof ExhaustedDataLoaderDispatchStrategy) {
if (dfeInternalState.getDataLoaderDispatchStrategy() instanceof PerLevelDataLoaderDispatchStrategy) {
AlternativeCallContext alternativeCallContext = dfeInternalState.getDeferredCallContext();
int level = dfe.getExecutionStepInfo().getPath().getLevel();
String path = dfe.getExecutionStepInfo().getPath().toString();
((ExhaustedDataLoaderDispatchStrategy) dfeInternalState.dataLoaderDispatchStrategy).newDataLoaderInvocation(path, level, delegate, dataLoaderName, key, alternativeCallContext);
((PerLevelDataLoaderDispatchStrategy) dfeInternalState.dataLoaderDispatchStrategy).newDataLoaderInvocation(path, level, delegate, dataLoaderName, key, alternativeCallContext);
} else if (dfeInternalState.getDataLoaderDispatchStrategy() instanceof ExhaustedDataLoaderDispatchStrategy) {
AlternativeCallContext alternativeCallContext = dfeInternalState.getDeferredCallContext();
((ExhaustedDataLoaderDispatchStrategy) dfeInternalState.dataLoaderDispatchStrategy).newDataLoaderInvocation(alternativeCallContext);
}
return result;
}
Expand Down