Skip to content
Closed
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
16 changes: 15 additions & 1 deletion src/main/java/graphql/ExecutionInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class ExecutionInput {
// this is currently not used but we want it back soon after the v23 release
private final AtomicBoolean cancelled;

private final boolean profileExecution;

@Internal
private ExecutionInput(Builder builder) {
Expand All @@ -47,6 +48,7 @@ private ExecutionInput(Builder builder) {
this.localContext = builder.localContext;
this.extensions = builder.extensions;
this.cancelled = builder.cancelled;
this.profileExecution = builder.profileExecution;
}

/**
Expand Down Expand Up @@ -142,6 +144,10 @@ public Map<String, Object> getExtensions() {
return extensions;
}

public boolean isProfileExecution() {
return profileExecution;
}

/**
* This helps you transform the current ExecutionInput object into another one by starting a builder with all
* the current values and allows you to transform it how you want.
Expand All @@ -163,7 +169,9 @@ public ExecutionInput transform(Consumer<Builder> builderConsumer) {
.variables(this.rawVariables.toMap())
.extensions(this.extensions)
.executionId(this.executionId)
.locale(this.locale);
.locale(this.locale)
.profileExecution(this.profileExecution);


builderConsumer.accept(builder);

Expand Down Expand Up @@ -221,6 +229,7 @@ public static class Builder {
private Locale locale = Locale.getDefault();
private ExecutionId executionId;
private AtomicBoolean cancelled = new AtomicBoolean(false);
private boolean profileExecution;

public Builder query(String query) {
this.query = assertNotNull(query, () -> "query can't be null");
Expand Down Expand Up @@ -360,6 +369,11 @@ public Builder dataLoaderRegistry(DataLoaderRegistry dataLoaderRegistry) {
return this;
}

public Builder profileExecution(boolean profileExecution) {
this.profileExecution = profileExecution;
return this;
}

public ExecutionInput build() {
return new ExecutionInput(this);
}
Expand Down
24 changes: 14 additions & 10 deletions src/main/java/graphql/GraphQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,9 @@ public CompletableFuture<ExecutionResult> executeAsync(UnaryOperator<ExecutionIn
* @return a promise to an {@link ExecutionResult} which can include errors
*/
public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionInput) {
Profiler profiler = executionInput.isProfileExecution() ? new ProfilerImpl() : Profiler.NO_OP;
profiler.start();

ExecutionInput executionInputWithId = ensureInputHasId(executionInput);

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

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

CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(instrumentedExecutionInput, graphQLSchema, instrumentationState);
CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(instrumentedExecutionInput, graphQLSchema, instrumentationState, profiler);
//
// finish up instrumentation
executionResult = executionResult.whenComplete(completeInstrumentationCtxCF(executionInstrumentation));
Expand Down Expand Up @@ -460,27 +463,27 @@ private ExecutionInput ensureInputHasId(ExecutionInput executionInput) {
}


private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, Profiler profiler) {
AtomicReference<ExecutionInput> executionInputRef = new AtomicReference<>(executionInput);
Function<ExecutionInput, PreparsedDocumentEntry> computeFunction = transformedInput -> {
// if they change the original query in the pre-parser, then we want to see it downstream from then on
executionInputRef.set(transformedInput);
return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState);
return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState, profiler);
};
CompletableFuture<PreparsedDocumentEntry> preparsedDoc = preparsedDocumentProvider.getDocumentAsync(executionInput, computeFunction);
return preparsedDoc.thenCompose(preparsedDocumentEntry -> {
if (preparsedDocumentEntry.hasErrors()) {
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDocumentEntry.getErrors()));
}
try {
return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState);
return executeImpl(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState, profiler);
} catch (AbortExecutionException e) {
return CompletableFuture.completedFuture(e.toExecutionResult());
}
});
}

private PreparsedDocumentEntry parseAndValidate(AtomicReference<ExecutionInput> executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
private PreparsedDocumentEntry parseAndValidate(AtomicReference<ExecutionInput> executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, Profiler profiler) {

ExecutionInput executionInput = executionInputRef.get();

Expand Down Expand Up @@ -533,13 +536,14 @@ private List<ValidationError> validate(ExecutionInput executionInput, Document d
return validationErrors;
}

private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput,
Document document,
GraphQLSchema graphQLSchema,
InstrumentationState instrumentationState
private CompletableFuture<ExecutionResult> executeImpl(ExecutionInput executionInput,
Document document,
GraphQLSchema graphQLSchema,
InstrumentationState instrumentationState,
Profiler profiler
) {

Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader);
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader, profiler);
ExecutionId executionId = executionInput.getExecutionId();

return execution.execute(document, graphQLSchema, executionId, executionInput, instrumentationState);
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/graphql/Profiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package graphql;

import graphql.schema.DataFetcher;
import org.jspecify.annotations.NullMarked;

@Internal
@NullMarked
public interface Profiler {

Profiler NO_OP = new Profiler() {
};

default void start() {

}


default void rootFieldCount(int size) {

}

default void subSelectionCount(int size) {

}

default void fieldFetched(Object fetchedObject, DataFetcher<?> dataFetcher) {

}
}
37 changes: 37 additions & 0 deletions src/main/java/graphql/ProfilerImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package graphql;

import graphql.schema.DataFetcher;
import graphql.schema.PropertyDataFetcher;
import org.jspecify.annotations.NullMarked;

import java.util.concurrent.atomic.AtomicInteger;

@Internal
@NullMarked
public class ProfilerImpl implements Profiler {

volatile long startTime;
volatile int rootFieldCount;

AtomicInteger fieldCount;
AtomicInteger propertyDataFetcherCount;

@Override
public void start() {
startTime = System.nanoTime();
}


@Override
public void rootFieldCount(int count) {
this.rootFieldCount = count;
}

@Override
public void fieldFetched(Object fetchedObject, DataFetcher<?> dataFetcher) {
fieldCount.incrementAndGet();
if (dataFetcher instanceof PropertyDataFetcher) {
propertyDataFetcherCount.incrementAndGet();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont

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

BiConsumer<List<Object>, Throwable> handleResultsConsumer = handleResults(executionContext, fieldsExecutedOnInitialResult, overallResult);
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import graphql.GraphQLContext;
import graphql.GraphQLError;
import graphql.Internal;
import graphql.Profiler;
import graphql.execution.incremental.IncrementalCallState;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
Expand Down Expand Up @@ -57,19 +58,22 @@ public class Execution {
private final Instrumentation instrumentation;
private final ValueUnboxer valueUnboxer;
private final boolean doNotAutomaticallyDispatchDataLoader;
private final Profiler profiler;

public Execution(ExecutionStrategy queryStrategy,
ExecutionStrategy mutationStrategy,
ExecutionStrategy subscriptionStrategy,
Instrumentation instrumentation,
ValueUnboxer valueUnboxer,
boolean doNotAutomaticallyDispatchDataLoader) {
boolean doNotAutomaticallyDispatchDataLoader,
Profiler profiler) {
this.queryStrategy = queryStrategy != null ? queryStrategy : new AsyncExecutionStrategy();
this.mutationStrategy = mutationStrategy != null ? mutationStrategy : new AsyncSerialExecutionStrategy();
this.subscriptionStrategy = subscriptionStrategy != null ? subscriptionStrategy : new AsyncExecutionStrategy();
this.instrumentation = instrumentation;
this.valueUnboxer = valueUnboxer;
this.doNotAutomaticallyDispatchDataLoader = doNotAutomaticallyDispatchDataLoader;
this.profiler = profiler;
}

public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSchema graphQLSchema, ExecutionId executionId, ExecutionInput executionInput, InstrumentationState instrumentationState) {
Expand Down Expand Up @@ -115,6 +119,7 @@ public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSche
.executionInput(executionInput)
.propagapropagateErrorsOnNonNullContractFailureeErrors(propagateErrorsOnNonNullContractFailure)
.engineRunningObserver(engineRunningObserver)
.profiler(profiler)
.build();

executionContext.getGraphQLContext().put(ResultNodesInfo.RESULT_NODES_INFO, executionContext.getResultNodesInfo());
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/graphql/execution/ExecutionContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import graphql.GraphQLContext;
import graphql.GraphQLError;
import graphql.Internal;
import graphql.Profiler;
import graphql.PublicApi;
import graphql.collect.ImmutableKit;
import graphql.execution.EngineRunningObserver.RunningState;
Expand Down Expand Up @@ -78,6 +79,7 @@ public class ExecutionContext {

private final ResultNodesInfo resultNodesInfo = new ResultNodesInfo();
private final EngineRunningObserver engineRunningObserver;
private final Profiler profiler;

ExecutionContext(ExecutionContextBuilder builder) {
this.graphQLSchema = builder.graphQLSchema;
Expand Down Expand Up @@ -105,6 +107,7 @@ public class ExecutionContext {
this.queryTree = FpKit.interThreadMemoize(() -> ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables));
this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure;
this.engineRunningObserver = builder.engineRunningObserver;
this.profiler = builder.profiler;
}


Expand Down Expand Up @@ -440,4 +443,8 @@ private void changeOfState(RunningState runningState) {
engineRunningObserver.runningStateChanged(executionId, graphQLContext, runningState);
}
}

public Profiler getProfiler() {
return profiler;
}
}
8 changes: 8 additions & 0 deletions src/main/java/graphql/execution/ExecutionContextBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import graphql.GraphQLContext;
import graphql.GraphQLError;
import graphql.Internal;
import graphql.Profiler;
import graphql.collect.ImmutableKit;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationState;
Expand Down Expand Up @@ -51,6 +52,7 @@ public class ExecutionContextBuilder {
DataLoaderDispatchStrategy dataLoaderDispatcherStrategy = DataLoaderDispatchStrategy.NO_OP;
boolean propagateErrorsOnNonNullContractFailure = true;
EngineRunningObserver engineRunningObserver;
Profiler profiler;

/**
* @return a new builder of {@link graphql.execution.ExecutionContext}s
Expand Down Expand Up @@ -99,6 +101,7 @@ public ExecutionContextBuilder() {
dataLoaderDispatcherStrategy = other.getDataLoaderDispatcherStrategy();
propagateErrorsOnNonNullContractFailure = other.propagateErrorsOnNonNullContractFailure();
engineRunningObserver = other.getEngineRunningObserver();
profiler = other.getProfiler();
}

public ExecutionContextBuilder instrumentation(Instrumentation instrumentation) {
Expand Down Expand Up @@ -245,4 +248,9 @@ public ExecutionContextBuilder engineRunningObserver(EngineRunningObserver engin
this.engineRunningObserver = engineRunningObserver;
return this;
}

public ExecutionContextBuilder profiler(Profiler profiler) {
this.profiler = profiler;
return this;
}
}
3 changes: 3 additions & 0 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ protected Object executeObject(ExecutionContext executionContext, ExecutionStrat
if (fieldValueInfosResult instanceof CompletableFuture) {
CompletableFuture<List<FieldValueInfo>> fieldValueInfos = (CompletableFuture<List<FieldValueInfo>>) fieldValueInfosResult;
fieldValueInfos.whenComplete((completeValueInfos, throwable) -> {
executionContext.getProfiler().subSelectionCount(completeValueInfos.size());
executionContext.run(throwable, () -> {
if (throwable != null) {
handleResultsConsumer.accept(null, throwable);
Expand All @@ -250,6 +251,7 @@ protected Object executeObject(ExecutionContext executionContext, ExecutionStrat
return overallResult;
} else {
List<FieldValueInfo> completeValueInfos = (List<FieldValueInfo>) fieldValueInfosResult;
executionContext.getProfiler().subSelectionCount(completeValueInfos.size());

Async.CombinedBuilder<Object> resultFutures = fieldValuesCombinedBuilder(completeValueInfos);
dataLoaderDispatcherStrategy.executeObjectOnFieldValuesInfo(completeValueInfos, parameters);
Expand Down Expand Up @@ -478,6 +480,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
executionContext.getDataLoaderDispatcherStrategy().fieldFetched(executionContext, parameters, dataFetcher, fetchedObject);
fetchCtx.onDispatched();
fetchCtx.onFetchedValue(fetchedObject);
executionContext.getProfiler().fieldFetched(fetchedObject, dataFetcher);
// if it's a subscription, leave any reactive objects alone
if (!executionContext.isSubscriptionOperation()) {
// possible convert reactive objects into CompletableFutures
Expand Down
4 changes: 2 additions & 2 deletions src/test/groovy/graphql/execution/ExecutionTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class ExecutionTest extends Specification {
def subscriptionStrategy = new CountingExecutionStrategy()
def mutationStrategy = new CountingExecutionStrategy()
def queryStrategy = new CountingExecutionStrategy()
def execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, SimplePerformantInstrumentation.INSTANCE, ValueUnboxer.DEFAULT, false)
def execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, SimplePerformantInstrumentation.INSTANCE, ValueUnboxer.DEFAULT, false, profiler)
def emptyExecutionInput = ExecutionInput.newExecutionInput().query("query").build()
def instrumentationState = new InstrumentationState() {}

Expand Down Expand Up @@ -124,7 +124,7 @@ class ExecutionTest extends Specification {
}
}

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


when:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import graphql.execution.ExecutionId
import graphql.execution.ResultPath
import graphql.execution.ValueUnboxer
import graphql.execution.instrumentation.ChainedInstrumentation
import graphql.execution.instrumentation.SimplePerformantInstrumentation
import graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters
import spock.lang.Specification

import java.util.concurrent.CompletableFuture
Expand Down Expand Up @@ -307,7 +305,7 @@ class FieldValidationTest extends Specification {
def document = TestUtil.parseQuery(query)
def strategy = new AsyncExecutionStrategy()
def instrumentation = new FieldValidationInstrumentation(validation)
def execution = new Execution(strategy, strategy, strategy, instrumentation, ValueUnboxer.DEFAULT, false)
def execution = new Execution(strategy, strategy, strategy, instrumentation, ValueUnboxer.DEFAULT, false, profiler)

def executionInput = ExecutionInput.newExecutionInput().query(query).variables(variables).build()
execution.execute(document, schema, ExecutionId.generate(), executionInput, null)
Expand Down
Loading