Skip to content

Commit 6a29b08

Browse files
committed
wip
1 parent 14c9136 commit 6a29b08

File tree

11 files changed

+123
-17
lines changed

11 files changed

+123
-17
lines changed

src/main/java/graphql/ExecutionInput.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class ExecutionInput {
3232
// this is currently not used but we want it back soon after the v23 release
3333
private final AtomicBoolean cancelled;
3434

35+
private final boolean profileExecution;
3536

3637
@Internal
3738
private ExecutionInput(Builder builder) {
@@ -47,6 +48,7 @@ private ExecutionInput(Builder builder) {
4748
this.localContext = builder.localContext;
4849
this.extensions = builder.extensions;
4950
this.cancelled = builder.cancelled;
51+
this.profileExecution = builder.profileExecution;
5052
}
5153

5254
/**
@@ -142,6 +144,10 @@ public Map<String, Object> getExtensions() {
142144
return extensions;
143145
}
144146

147+
public boolean isProfileExecution() {
148+
return profileExecution;
149+
}
150+
145151
/**
146152
* This helps you transform the current ExecutionInput object into another one by starting a builder with all
147153
* the current values and allows you to transform it how you want.
@@ -163,7 +169,9 @@ public ExecutionInput transform(Consumer<Builder> builderConsumer) {
163169
.variables(this.rawVariables.toMap())
164170
.extensions(this.extensions)
165171
.executionId(this.executionId)
166-
.locale(this.locale);
172+
.locale(this.locale)
173+
.profileExecution(this.profileExecution);
174+
167175

168176
builderConsumer.accept(builder);
169177

@@ -221,6 +229,7 @@ public static class Builder {
221229
private Locale locale = Locale.getDefault();
222230
private ExecutionId executionId;
223231
private AtomicBoolean cancelled = new AtomicBoolean(false);
232+
private boolean profileExecution;
224233

225234
public Builder query(String query) {
226235
this.query = assertNotNull(query, () -> "query can't be null");
@@ -360,6 +369,11 @@ public Builder dataLoaderRegistry(DataLoaderRegistry dataLoaderRegistry) {
360369
return this;
361370
}
362371

372+
public Builder profileExecution(boolean profileExecution) {
373+
this.profileExecution = profileExecution;
374+
return this;
375+
}
376+
363377
public ExecutionInput build() {
364378
return new ExecutionInput(this);
365379
}

src/main/java/graphql/GraphQL.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,9 @@ public CompletableFuture<ExecutionResult> executeAsync(UnaryOperator<ExecutionIn
412412
* @return a promise to an {@link ExecutionResult} which can include errors
413413
*/
414414
public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionInput) {
415+
Profiler profiler = executionInput.isProfileExecution() ? new ProfilerImpl() : Profiler.NO_OP;
416+
profiler.start();
417+
415418
ExecutionInput executionInputWithId = ensureInputHasId(executionInput);
416419

417420
CompletableFuture<InstrumentationState> instrumentationStateCF = instrumentation.createStateAsync(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInputWithId));
@@ -426,7 +429,7 @@ public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionI
426429

427430
GraphQLSchema graphQLSchema = instrumentation.instrumentSchema(this.graphQLSchema, instrumentationParameters, instrumentationState);
428431

429-
CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(instrumentedExecutionInput, graphQLSchema, instrumentationState);
432+
CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(instrumentedExecutionInput, graphQLSchema, instrumentationState, profiler);
430433
//
431434
// finish up instrumentation
432435
executionResult = executionResult.whenComplete(completeInstrumentationCtxCF(executionInstrumentation));
@@ -460,27 +463,27 @@ private ExecutionInput ensureInputHasId(ExecutionInput executionInput) {
460463
}
461464

462465

463-
private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
466+
private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, Profiler profiler) {
464467
AtomicReference<ExecutionInput> executionInputRef = new AtomicReference<>(executionInput);
465468
Function<ExecutionInput, PreparsedDocumentEntry> computeFunction = transformedInput -> {
466469
// if they change the original query in the pre-parser, then we want to see it downstream from then on
467470
executionInputRef.set(transformedInput);
468-
return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState);
471+
return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState, profiler);
469472
};
470473
CompletableFuture<PreparsedDocumentEntry> preparsedDoc = preparsedDocumentProvider.getDocumentAsync(executionInput, computeFunction);
471474
return preparsedDoc.thenCompose(preparsedDocumentEntry -> {
472475
if (preparsedDocumentEntry.hasErrors()) {
473476
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDocumentEntry.getErrors()));
474477
}
475478
try {
476-
return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState);
479+
return executeImpl(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState, profiler);
477480
} catch (AbortExecutionException e) {
478481
return CompletableFuture.completedFuture(e.toExecutionResult());
479482
}
480483
});
481484
}
482485

483-
private PreparsedDocumentEntry parseAndValidate(AtomicReference<ExecutionInput> executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
486+
private PreparsedDocumentEntry parseAndValidate(AtomicReference<ExecutionInput> executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, Profiler profiler) {
484487

485488
ExecutionInput executionInput = executionInputRef.get();
486489

@@ -533,13 +536,14 @@ private List<ValidationError> validate(ExecutionInput executionInput, Document d
533536
return validationErrors;
534537
}
535538

536-
private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput,
537-
Document document,
538-
GraphQLSchema graphQLSchema,
539-
InstrumentationState instrumentationState
539+
private CompletableFuture<ExecutionResult> executeImpl(ExecutionInput executionInput,
540+
Document document,
541+
GraphQLSchema graphQLSchema,
542+
InstrumentationState instrumentationState,
543+
Profiler profiler
540544
) {
541545

542-
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader);
546+
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader, profiler);
543547
ExecutionId executionId = executionInput.getExecutionId();
544548

545549
return execution.execute(document, graphQLSchema, executionId, executionInput, instrumentationState);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package graphql;
2+
3+
import graphql.schema.DataFetcher;
4+
import org.jspecify.annotations.NullMarked;
5+
6+
@Internal
7+
@NullMarked
8+
public interface Profiler {
9+
10+
Profiler NO_OP = new Profiler() {
11+
};
12+
13+
default void start() {
14+
15+
}
16+
17+
18+
default void rootFieldCount(int size) {
19+
20+
}
21+
22+
default void subSelectionCount(int size) {
23+
24+
}
25+
26+
default void fieldFetched(Object fetchedObject, DataFetcher<?> dataFetcher) {
27+
28+
}
29+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package graphql;
2+
3+
import graphql.schema.DataFetcher;
4+
import graphql.schema.PropertyDataFetcher;
5+
import org.jspecify.annotations.NullMarked;
6+
7+
import java.util.concurrent.atomic.AtomicInteger;
8+
9+
@Internal
10+
@NullMarked
11+
public class ProfilerImpl implements Profiler {
12+
13+
volatile long startTime;
14+
volatile int rootFieldCount;
15+
16+
AtomicInteger fieldCount;
17+
AtomicInteger propertyDataFetcherCount;
18+
19+
@Override
20+
public void start() {
21+
startTime = System.nanoTime();
22+
}
23+
24+
25+
@Override
26+
public void rootFieldCount(int count) {
27+
this.rootFieldCount = count;
28+
}
29+
30+
@Override
31+
public void fieldFetched(Object fetchedObject, DataFetcher<?> dataFetcher) {
32+
fieldCount.incrementAndGet();
33+
if (dataFetcher instanceof PropertyDataFetcher) {
34+
propertyDataFetcherCount.incrementAndGet();
35+
}
36+
}
37+
}

src/main/java/graphql/execution/AsyncExecutionStrategy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont
6262

6363
futures.await().whenComplete((completeValueInfos, throwable) -> {
6464
executionContext.run(throwable,() -> {
65+
executionContext.getProfiler().rootFieldCount(completeValueInfos.size());
6566
List<String> fieldsExecutedOnInitialResult = deferredExecutionSupport.getNonDeferredFieldNames(fieldNames);
6667

6768
BiConsumer<List<Object>, Throwable> handleResultsConsumer = handleResults(executionContext, fieldsExecutedOnInitialResult, overallResult);

src/main/java/graphql/execution/Execution.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import graphql.GraphQLContext;
1010
import graphql.GraphQLError;
1111
import graphql.Internal;
12+
import graphql.Profiler;
1213
import graphql.execution.incremental.IncrementalCallState;
1314
import graphql.execution.instrumentation.Instrumentation;
1415
import graphql.execution.instrumentation.InstrumentationContext;
@@ -57,19 +58,22 @@ public class Execution {
5758
private final Instrumentation instrumentation;
5859
private final ValueUnboxer valueUnboxer;
5960
private final boolean doNotAutomaticallyDispatchDataLoader;
61+
private final Profiler profiler;
6062

6163
public Execution(ExecutionStrategy queryStrategy,
6264
ExecutionStrategy mutationStrategy,
6365
ExecutionStrategy subscriptionStrategy,
6466
Instrumentation instrumentation,
6567
ValueUnboxer valueUnboxer,
66-
boolean doNotAutomaticallyDispatchDataLoader) {
68+
boolean doNotAutomaticallyDispatchDataLoader,
69+
Profiler profiler) {
6770
this.queryStrategy = queryStrategy != null ? queryStrategy : new AsyncExecutionStrategy();
6871
this.mutationStrategy = mutationStrategy != null ? mutationStrategy : new AsyncSerialExecutionStrategy();
6972
this.subscriptionStrategy = subscriptionStrategy != null ? subscriptionStrategy : new AsyncExecutionStrategy();
7073
this.instrumentation = instrumentation;
7174
this.valueUnboxer = valueUnboxer;
7275
this.doNotAutomaticallyDispatchDataLoader = doNotAutomaticallyDispatchDataLoader;
76+
this.profiler = profiler;
7377
}
7478

7579
public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSchema graphQLSchema, ExecutionId executionId, ExecutionInput executionInput, InstrumentationState instrumentationState) {
@@ -115,6 +119,7 @@ public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSche
115119
.executionInput(executionInput)
116120
.propagapropagateErrorsOnNonNullContractFailureeErrors(propagateErrorsOnNonNullContractFailure)
117121
.engineRunningObserver(engineRunningObserver)
122+
.profiler(profiler)
118123
.build();
119124

120125
executionContext.getGraphQLContext().put(ResultNodesInfo.RESULT_NODES_INFO, executionContext.getResultNodesInfo());

src/main/java/graphql/execution/ExecutionContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import graphql.GraphQLContext;
99
import graphql.GraphQLError;
1010
import graphql.Internal;
11+
import graphql.Profiler;
1112
import graphql.PublicApi;
1213
import graphql.collect.ImmutableKit;
1314
import graphql.execution.EngineRunningObserver.RunningState;
@@ -78,6 +79,7 @@ public class ExecutionContext {
7879

7980
private final ResultNodesInfo resultNodesInfo = new ResultNodesInfo();
8081
private final EngineRunningObserver engineRunningObserver;
82+
private final Profiler profiler;
8183

8284
ExecutionContext(ExecutionContextBuilder builder) {
8385
this.graphQLSchema = builder.graphQLSchema;
@@ -105,6 +107,7 @@ public class ExecutionContext {
105107
this.queryTree = FpKit.interThreadMemoize(() -> ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables));
106108
this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure;
107109
this.engineRunningObserver = builder.engineRunningObserver;
110+
this.profiler = builder.profiler;
108111
}
109112

110113

@@ -440,4 +443,8 @@ private void changeOfState(RunningState runningState) {
440443
engineRunningObserver.runningStateChanged(executionId, graphQLContext, runningState);
441444
}
442445
}
446+
447+
public Profiler getProfiler() {
448+
return profiler;
449+
}
443450
}

src/main/java/graphql/execution/ExecutionContextBuilder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import graphql.GraphQLContext;
88
import graphql.GraphQLError;
99
import graphql.Internal;
10+
import graphql.Profiler;
1011
import graphql.collect.ImmutableKit;
1112
import graphql.execution.instrumentation.Instrumentation;
1213
import graphql.execution.instrumentation.InstrumentationState;
@@ -51,6 +52,7 @@ public class ExecutionContextBuilder {
5152
DataLoaderDispatchStrategy dataLoaderDispatcherStrategy = DataLoaderDispatchStrategy.NO_OP;
5253
boolean propagateErrorsOnNonNullContractFailure = true;
5354
EngineRunningObserver engineRunningObserver;
55+
Profiler profiler;
5456

5557
/**
5658
* @return a new builder of {@link graphql.execution.ExecutionContext}s
@@ -99,6 +101,7 @@ public ExecutionContextBuilder() {
99101
dataLoaderDispatcherStrategy = other.getDataLoaderDispatcherStrategy();
100102
propagateErrorsOnNonNullContractFailure = other.propagateErrorsOnNonNullContractFailure();
101103
engineRunningObserver = other.getEngineRunningObserver();
104+
profiler = other.getProfiler();
102105
}
103106

104107
public ExecutionContextBuilder instrumentation(Instrumentation instrumentation) {
@@ -245,4 +248,9 @@ public ExecutionContextBuilder engineRunningObserver(EngineRunningObserver engin
245248
this.engineRunningObserver = engineRunningObserver;
246249
return this;
247250
}
251+
252+
public ExecutionContextBuilder profiler(Profiler profiler) {
253+
this.profiler = profiler;
254+
return this;
255+
}
248256
}

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ protected Object executeObject(ExecutionContext executionContext, ExecutionStrat
226226
if (fieldValueInfosResult instanceof CompletableFuture) {
227227
CompletableFuture<List<FieldValueInfo>> fieldValueInfos = (CompletableFuture<List<FieldValueInfo>>) fieldValueInfosResult;
228228
fieldValueInfos.whenComplete((completeValueInfos, throwable) -> {
229+
executionContext.getProfiler().subSelectionCount(completeValueInfos.size());
229230
executionContext.run(throwable, () -> {
230231
if (throwable != null) {
231232
handleResultsConsumer.accept(null, throwable);
@@ -250,6 +251,7 @@ protected Object executeObject(ExecutionContext executionContext, ExecutionStrat
250251
return overallResult;
251252
} else {
252253
List<FieldValueInfo> completeValueInfos = (List<FieldValueInfo>) fieldValueInfosResult;
254+
executionContext.getProfiler().subSelectionCount(completeValueInfos.size());
253255

254256
Async.CombinedBuilder<Object> resultFutures = fieldValuesCombinedBuilder(completeValueInfos);
255257
dataLoaderDispatcherStrategy.executeObjectOnFieldValuesInfo(completeValueInfos, parameters);
@@ -478,6 +480,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
478480
executionContext.getDataLoaderDispatcherStrategy().fieldFetched(executionContext, parameters, dataFetcher, fetchedObject);
479481
fetchCtx.onDispatched();
480482
fetchCtx.onFetchedValue(fetchedObject);
483+
executionContext.getProfiler().fieldFetched(fetchedObject, dataFetcher);
481484
// if it's a subscription, leave any reactive objects alone
482485
if (!executionContext.isSubscriptionOperation()) {
483486
// possible convert reactive objects into CompletableFutures

src/test/groovy/graphql/execution/ExecutionTest.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class ExecutionTest extends Specification {
3535
def subscriptionStrategy = new CountingExecutionStrategy()
3636
def mutationStrategy = new CountingExecutionStrategy()
3737
def queryStrategy = new CountingExecutionStrategy()
38-
def execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, SimplePerformantInstrumentation.INSTANCE, ValueUnboxer.DEFAULT, false)
38+
def execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, SimplePerformantInstrumentation.INSTANCE, ValueUnboxer.DEFAULT, false, profiler)
3939
def emptyExecutionInput = ExecutionInput.newExecutionInput().query("query").build()
4040
def instrumentationState = new InstrumentationState() {}
4141

@@ -124,7 +124,7 @@ class ExecutionTest extends Specification {
124124
}
125125
}
126126

127-
def execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, ValueUnboxer.DEFAULT, false)
127+
def execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, ValueUnboxer.DEFAULT, false, profiler)
128128

129129

130130
when:

0 commit comments

Comments
 (0)