Skip to content

Commit 29f8220

Browse files
committed
Cherry pick 3934 for execution step info builder changes
1 parent 5d85e48 commit 29f8220

File tree

4 files changed

+199
-54
lines changed

4 files changed

+199
-54
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package graphql.execution;
2+
3+
import graphql.Scalars;
4+
import graphql.language.Field;
5+
import org.openjdk.jmh.annotations.Benchmark;
6+
import org.openjdk.jmh.annotations.BenchmarkMode;
7+
import org.openjdk.jmh.annotations.Fork;
8+
import org.openjdk.jmh.annotations.Level;
9+
import org.openjdk.jmh.annotations.Measurement;
10+
import org.openjdk.jmh.annotations.Mode;
11+
import org.openjdk.jmh.annotations.OutputTimeUnit;
12+
import org.openjdk.jmh.annotations.Param;
13+
import org.openjdk.jmh.annotations.Scope;
14+
import org.openjdk.jmh.annotations.Setup;
15+
import org.openjdk.jmh.annotations.State;
16+
import org.openjdk.jmh.annotations.TearDown;
17+
import org.openjdk.jmh.annotations.Warmup;
18+
import org.openjdk.jmh.infra.Blackhole;
19+
import org.openjdk.jmh.profile.GCProfiler;
20+
import org.openjdk.jmh.runner.Runner;
21+
import org.openjdk.jmh.runner.options.Options;
22+
import org.openjdk.jmh.runner.options.OptionsBuilder;
23+
24+
import java.util.concurrent.TimeUnit;
25+
26+
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
27+
28+
@State(Scope.Benchmark)
29+
@Warmup(iterations = 2, time = 5)
30+
@Measurement(iterations = 2)
31+
@Fork(2)
32+
public class ExecutionStepInfoBenchmark {
33+
@Param({"1000000", "2000000"})
34+
int howManyItems = 1000000;
35+
36+
@Setup(Level.Trial)
37+
public void setUp() {
38+
}
39+
40+
@TearDown(Level.Trial)
41+
public void tearDown() {
42+
}
43+
44+
45+
MergedField mergedField = MergedField.newMergedField().addField(Field.newField("f").build()).build();
46+
47+
ResultPath path = ResultPath.rootPath().segment("f");
48+
ExecutionStepInfo rootStepInfo = newExecutionStepInfo()
49+
.path(path).type(Scalars.GraphQLString)
50+
.field(mergedField)
51+
.build();
52+
53+
54+
@Benchmark
55+
@BenchmarkMode(Mode.Throughput)
56+
@OutputTimeUnit(TimeUnit.SECONDS)
57+
public void benchMarkDirectConstructorThroughput(Blackhole blackhole) {
58+
for (int i = 0; i < howManyItems; i++) {
59+
ResultPath newPath = path.segment(1);
60+
ExecutionStepInfo newOne = rootStepInfo.transform(Scalars.GraphQLInt, rootStepInfo, newPath);
61+
blackhole.consume(newOne);
62+
}
63+
}
64+
65+
@Benchmark
66+
@BenchmarkMode(Mode.Throughput)
67+
@OutputTimeUnit(TimeUnit.SECONDS)
68+
public void benchMarkBuilderThroughput(Blackhole blackhole) {
69+
for (int i = 0; i < howManyItems; i++) {
70+
ResultPath newPath = path.segment(1);
71+
ExecutionStepInfo newOne = newExecutionStepInfo(rootStepInfo).parentInfo(rootStepInfo)
72+
.type(Scalars.GraphQLInt).path(newPath).build();
73+
blackhole.consume(newOne);
74+
}
75+
}
76+
77+
public static void main(String[] args) throws Exception {
78+
Options opt = new OptionsBuilder()
79+
.include("graphql.execution.ExecutionStepInfoBenchmark")
80+
.addProfiler(GCProfiler.class)
81+
.build();
82+
83+
new Runner(opt).run();
84+
}
85+
86+
}

src/main/java/graphql/execution/ExecutionStepInfo.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package graphql.execution;
22

3+
import graphql.Internal;
34
import graphql.PublicApi;
45
import graphql.collect.ImmutableMapWithNullValues;
56
import graphql.schema.GraphQLFieldDefinition;
@@ -77,6 +78,25 @@ private ExecutionStepInfo(Builder builder) {
7778
this.fieldContainer = builder.fieldContainer;
7879
}
7980

81+
/*
82+
* This constructor allows for a slightly ( 1% ish) faster transformation without an intermediate Builder object
83+
*/
84+
private ExecutionStepInfo(GraphQLOutputType type,
85+
ResultPath path,
86+
ExecutionStepInfo parent,
87+
MergedField field,
88+
GraphQLFieldDefinition fieldDefinition,
89+
GraphQLObjectType fieldContainer,
90+
Supplier<ImmutableMapWithNullValues<String, Object>> arguments) {
91+
this.type = assertNotNull(type, () -> "you must provide a graphql type");
92+
this.path = path;
93+
this.parent = parent;
94+
this.field = field;
95+
this.fieldDefinition = fieldDefinition;
96+
this.fieldContainer = fieldContainer;
97+
this.arguments = arguments;
98+
}
99+
80100
/**
81101
* The GraphQLObjectType where fieldDefinition is defined.
82102
* Note:
@@ -193,13 +213,12 @@ public boolean hasParent() {
193213
public ExecutionStepInfo changeTypeWithPreservedNonNull(GraphQLOutputType newType) {
194214
assertTrue(!GraphQLTypeUtil.isNonNull(newType), () -> "newType can't be non null");
195215
if (isNonNullType()) {
196-
return newExecutionStepInfo(this).type(GraphQLNonNull.nonNull(newType)).build();
216+
return transform(GraphQLNonNull.nonNull(newType));
197217
} else {
198-
return newExecutionStepInfo(this).type(newType).build();
218+
return transform(newType);
199219
}
200220
}
201221

202-
203222
/**
204223
* @return the type in graphql SDL format, eg [typeName!]!
205224
*/
@@ -216,6 +235,16 @@ public String toString() {
216235
'}';
217236
}
218237

238+
@Internal
239+
ExecutionStepInfo transform(GraphQLOutputType type) {
240+
return new ExecutionStepInfo(type, path, parent, field, fieldDefinition, fieldContainer, arguments);
241+
}
242+
243+
@Internal
244+
ExecutionStepInfo transform(GraphQLOutputType type, ExecutionStepInfo parent, ResultPath path) {
245+
return new ExecutionStepInfo(type, path, parent, field, fieldDefinition, fieldContainer, arguments);
246+
}
247+
219248
public ExecutionStepInfo transform(Consumer<Builder> builderConsumer) {
220249
Builder builder = new Builder(this);
221250
builderConsumer.accept(builder);
Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,92 @@
11
package graphql.execution;
22

33
import graphql.Internal;
4+
import graphql.collect.ImmutableMapWithNullValues;
5+
import graphql.language.Argument;
6+
import graphql.schema.GraphQLArgument;
7+
import graphql.schema.GraphQLCodeRegistry;
8+
import graphql.schema.GraphQLFieldDefinition;
49
import graphql.schema.GraphQLList;
10+
import graphql.schema.GraphQLObjectType;
511
import graphql.schema.GraphQLOutputType;
12+
import graphql.util.FpKit;
13+
import org.jspecify.annotations.NonNull;
14+
import org.jspecify.annotations.NullMarked;
15+
import org.jspecify.annotations.Nullable;
16+
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.function.Supplier;
20+
21+
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
622

723
@Internal
24+
@NullMarked
825
public class ExecutionStepInfoFactory {
926

1027
public ExecutionStepInfo newExecutionStepInfoForListElement(ExecutionStepInfo executionInfo, ResultPath indexedPath) {
1128
GraphQLList fieldType = (GraphQLList) executionInfo.getUnwrappedNonNullType();
1229
GraphQLOutputType typeInList = (GraphQLOutputType) fieldType.getWrappedType();
13-
return executionInfo.transform(builder -> builder
14-
.parentInfo(executionInfo)
15-
.type(typeInList)
16-
.path(indexedPath));
30+
return executionInfo.transform(typeInList, executionInfo, indexedPath);
31+
}
32+
33+
/**
34+
* Builds the type info hierarchy for the current field
35+
*
36+
* @param executionContext the execution context in play
37+
* @param parameters contains the parameters holding the fields to be executed and source object
38+
* @param fieldDefinition the field definition to build type info for
39+
* @param fieldContainer the field container
40+
*
41+
* @return a new type info
42+
*/
43+
public ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionContext,
44+
ExecutionStrategyParameters parameters,
45+
GraphQLFieldDefinition fieldDefinition,
46+
@Nullable GraphQLObjectType fieldContainer) {
47+
MergedField field = parameters.getField();
48+
ExecutionStepInfo parentStepInfo = parameters.getExecutionStepInfo();
49+
GraphQLOutputType fieldType = fieldDefinition.getType();
50+
List<GraphQLArgument> fieldArgDefs = fieldDefinition.getArguments();
51+
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues = ImmutableMapWithNullValues::emptyMap;
52+
//
53+
// no need to create args at all if there are none on the field def
54+
//
55+
if (!fieldArgDefs.isEmpty()) {
56+
argumentValues = getArgumentValues(executionContext, fieldArgDefs, field.getArguments());
57+
}
58+
59+
60+
return newExecutionStepInfo()
61+
.type(fieldType)
62+
.fieldDefinition(fieldDefinition)
63+
.fieldContainer(fieldContainer)
64+
.field(field)
65+
.path(parameters.getPath())
66+
.parentInfo(parentStepInfo)
67+
.arguments(argumentValues)
68+
.build();
1769
}
1870

71+
@NonNull
72+
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
73+
List<GraphQLArgument> fieldArgDefs,
74+
List<Argument> fieldArgs) {
75+
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues;
76+
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
77+
Supplier<ImmutableMapWithNullValues<String, Object>> argValuesSupplier = () -> {
78+
Map<String, Object> resolvedValues = ValuesResolver.getArgumentValues(codeRegistry,
79+
fieldArgDefs,
80+
fieldArgs,
81+
executionContext.getCoercedVariables(),
82+
executionContext.getGraphQLContext(),
83+
executionContext.getLocale());
84+
85+
return ImmutableMapWithNullValues.copyOf(resolvedValues);
86+
};
87+
argumentValues = FpKit.intraThreadMemoize(argValuesSupplier);
88+
return argumentValues;
89+
}
90+
91+
1992
}

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

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import graphql.TrivialDataFetcher;
1515
import graphql.TypeMismatchError;
1616
import graphql.UnresolvedTypeError;
17-
import graphql.collect.ImmutableMapWithNullValues;
1817
import graphql.execution.directives.QueryDirectives;
1918
import graphql.execution.directives.QueryDirectivesImpl;
2019
import graphql.execution.incremental.DeferredExecutionSupport;
@@ -29,7 +28,6 @@
2928
import graphql.execution.reactive.ReactiveSupport;
3029
import graphql.extensions.ExtensionsBuilder;
3130
import graphql.introspection.Introspection;
32-
import graphql.language.Argument;
3331
import graphql.language.Field;
3432
import graphql.normalized.ExecutableNormalizedField;
3533
import graphql.normalized.ExecutableNormalizedOperation;
@@ -38,12 +36,10 @@
3836
import graphql.schema.DataFetchingEnvironment;
3937
import graphql.schema.DataFetchingFieldSelectionSet;
4038
import graphql.schema.DataFetchingFieldSelectionSetImpl;
41-
import graphql.schema.GraphQLArgument;
4239
import graphql.schema.GraphQLCodeRegistry;
4340
import graphql.schema.GraphQLEnumType;
4441
import graphql.schema.GraphQLFieldDefinition;
4542
import graphql.schema.GraphQLObjectType;
46-
import graphql.schema.GraphQLOutputType;
4743
import graphql.schema.GraphQLScalarType;
4844
import graphql.schema.GraphQLSchema;
4945
import graphql.schema.GraphQLType;
@@ -64,7 +60,6 @@
6460
import java.util.function.Supplier;
6561

6662
import static graphql.execution.Async.exceptionallyCompletedFuture;
67-
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
6863
import static graphql.execution.FieldCollectorParameters.newParameters;
6964
import static graphql.execution.FieldValueInfo.CompleteValueType.ENUM;
7065
import static graphql.execution.FieldValueInfo.CompleteValueType.LIST;
@@ -1094,48 +1089,10 @@ protected ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionCo
10941089
ExecutionStrategyParameters parameters,
10951090
GraphQLFieldDefinition fieldDefinition,
10961091
GraphQLObjectType fieldContainer) {
1097-
MergedField field = parameters.getField();
1098-
ExecutionStepInfo parentStepInfo = parameters.getExecutionStepInfo();
1099-
GraphQLOutputType fieldType = fieldDefinition.getType();
1100-
List<GraphQLArgument> fieldArgDefs = fieldDefinition.getArguments();
1101-
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues = ImmutableMapWithNullValues::emptyMap;
1102-
//
1103-
// no need to create args at all if there are none on the field def
1104-
//
1105-
if (!fieldArgDefs.isEmpty()) {
1106-
argumentValues = getArgumentValues(executionContext, fieldArgDefs, field.getArguments());
1107-
}
1108-
1109-
1110-
return newExecutionStepInfo()
1111-
.type(fieldType)
1112-
.fieldDefinition(fieldDefinition)
1113-
.fieldContainer(fieldContainer)
1114-
.field(field)
1115-
.path(parameters.getPath())
1116-
.parentInfo(parentStepInfo)
1117-
.arguments(argumentValues)
1118-
.build();
1119-
}
1120-
1121-
@NonNull
1122-
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
1123-
List<GraphQLArgument> fieldArgDefs,
1124-
List<Argument> fieldArgs) {
1125-
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues;
1126-
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
1127-
Supplier<ImmutableMapWithNullValues<String, Object>> argValuesSupplier = () -> {
1128-
Map<String, Object> resolvedValues = ValuesResolver.getArgumentValues(codeRegistry,
1129-
fieldArgDefs,
1130-
fieldArgs,
1131-
executionContext.getCoercedVariables(),
1132-
executionContext.getGraphQLContext(),
1133-
executionContext.getLocale());
1134-
1135-
return ImmutableMapWithNullValues.copyOf(resolvedValues);
1136-
};
1137-
argumentValues = FpKit.intraThreadMemoize(argValuesSupplier);
1138-
return argumentValues;
1092+
return executionStepInfoFactory.createExecutionStepInfo(executionContext,
1093+
parameters,
1094+
fieldDefinition,
1095+
fieldContainer);
11391096
}
11401097

11411098
// Errors that result from the execution of deferred fields are kept in the deferred context only.

0 commit comments

Comments
 (0)