// Compile & run with graphql-java 25.0 and java-dataloader 6.0.0 on the classpath.
// Expected output:
// dataLoaderChainingEnabled=true
// dataLoaderLoadInvocations={} <-- BUG: empty (Profiler.dataLoaderUsed never called)
// dispatchEvents=[] <-- BUG: empty (batchLoadedNewStrategy/batchLoadedOldStrategy/manualDispatch never called)
// oldStrategyDispatchingAll=[...] <-- non-empty only if chaining=false (the one wired DataLoader-related event)
// fieldsFetched=[/users, /users[*]/name] <-- this works; demonstrates the Profiler is hooked up
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.ProfilerResult;
import graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.dataloader.BatchLoader;
import org.dataloader.DataLoader;
import org.dataloader.DataLoaderFactory;
import org.dataloader.DataLoaderRegistry;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public final class ProfilerUnwiredReproducer {
private static final String SDL = """
type Query { users: [User!]! }
type User { id: ID!, name: String! }
""";
public static void main(String[] args) {
final boolean chaining = args.length == 0 || !args[0].equals("--no-chaining");
// 1. Schema with one DataLoader-backed field.
final BatchLoader<String, String> nameLoader =
keys -> CompletableFuture.completedFuture(keys.stream().map(k -> "name-" + k).toList());
final RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()
.type("Query", t -> t.dataFetcher("users",
env -> List.of(new User("1"), new User("2"), new User("3"))))
.type("User", t -> t.dataFetcher("name",
env -> env.<DataLoader<String, String>>getDataLoader("nameLoader")
.load(((User) env.getSource()).id)))
.build();
final TypeDefinitionRegistry tdr = new SchemaParser().parse(SDL);
final GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(tdr, wiring);
final GraphQL graphQL = GraphQL.newGraphQL(schema).build();
// 2. ExecutionInput with profileExecution(true) + chaining flag.
final DataLoaderRegistry registry = new DataLoaderRegistry();
registry.register("nameLoader", DataLoaderFactory.newDataLoader(nameLoader));
final ExecutionInput input = ExecutionInput.newExecutionInput()
.query("{ users { id name } }")
.dataLoaderRegistry(registry)
.profileExecution(true)
.graphQLContext(b -> b.put(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING, chaining))
.build();
// 3. Execute and read the ProfilerResult.
final ExecutionResult result = graphQL.execute(input);
if (!result.getErrors().isEmpty()) {
System.err.println("Query errors: " + result.getErrors());
System.exit(1);
}
final ProfilerResult profile = input.getGraphQLContext().get(ProfilerResult.PROFILER_CONTEXT_KEY);
if (profile == null) {
System.err.println("ProfilerResult is null — profileExecution(true) was not honored");
System.exit(2);
}
// 4. Print the four collections that demonstrate the bug.
System.out.println("dataLoaderChainingEnabled = " + profile.isDataLoaderChainingEnabled());
System.out.println("fieldsFetched = " + profile.getFieldsFetched()); // populated (fieldFetched is wired)
System.out.println("oldStrategyDispatchingAll = " + profile.getOldStrategyDispatchingAll());// populated only when chaining=false
System.out.println("dataLoaderLoadInvocations = " + profile.getDataLoaderLoadInvocations());// EMPTY: dataLoaderUsed has 0 call sites
System.out.println("dispatchEvents = " + profile.getDispatchEvents()); // EMPTY: batchLoadedOld/NewStrategy + manualDispatch all have 0 call sites
}
private record User(String id) {}
}
Describe the bug
The Profiler interface declares 9 methods. In graphql-java-25.0 and on master as of today, 4 of them have zero call sites anywhere in the graphql-java codebase:
Profiler.dataLoaderUsed(String)(line)Profiler.batchLoadedOldStrategy(String, int, int)(line)Profiler.batchLoadedNewStrategy(String, Integer, int, boolean, boolean)(line)Profiler.manualDispatch(String, int, int)(line)That results in Profiler not collecting data relevant to dataloaders usage and
ProfilerResult.getDataLoaderLoadInvocationsandProfilerResult.getDispatchEventsalways return empty result.To Reproduce