Skip to content

Commit ecaebb1

Browse files
authored
Merge pull request #4040 from graphql-java/is-possible-type-improvements
Improved access speed of isPossibleType
2 parents ecd84a1 + d0cbc63 commit ecaebb1

18 files changed

+557
-165
lines changed

bin/jmh.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
4+
JAR="build/libs/graphql-java-0.0.0-$BRANCH-SNAPSHOT-jmh.jar"
5+
echo "build and then running jmh for $JAR"
6+
7+
./gradlew clean jmhJar
8+
9+
java -jar "$JAR" "$@"
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package benchmark;
2+
3+
import graphql.schema.GraphQLSchema;
4+
import graphql.schema.idl.RuntimeWiring;
5+
import graphql.schema.idl.SchemaGenerator;
6+
import graphql.schema.idl.SchemaParser;
7+
import graphql.schema.idl.TypeDefinitionRegistry;
8+
import org.openjdk.jmh.annotations.Benchmark;
9+
import org.openjdk.jmh.annotations.BenchmarkMode;
10+
import org.openjdk.jmh.annotations.Fork;
11+
import org.openjdk.jmh.annotations.Measurement;
12+
import org.openjdk.jmh.annotations.Mode;
13+
import org.openjdk.jmh.annotations.OutputTimeUnit;
14+
import org.openjdk.jmh.annotations.Warmup;
15+
import org.openjdk.jmh.infra.Blackhole;
16+
import org.openjdk.jmh.runner.Runner;
17+
import org.openjdk.jmh.runner.RunnerException;
18+
import org.openjdk.jmh.runner.options.Options;
19+
import org.openjdk.jmh.runner.options.OptionsBuilder;
20+
21+
import java.util.concurrent.TimeUnit;
22+
23+
import static benchmark.BenchmarkUtils.runInToolingForSomeTimeThenExit;
24+
25+
/**
26+
* This JMH
27+
*/
28+
@Warmup(iterations = 2, time = 5)
29+
@Measurement(iterations = 3)
30+
@Fork(3)
31+
public class CreateExtendedSchemaBenchmark {
32+
33+
private static final String SDL = mkSDL();
34+
35+
@Benchmark
36+
@BenchmarkMode(Mode.Throughput)
37+
@OutputTimeUnit(TimeUnit.MINUTES)
38+
public void benchmarkLargeSchemaCreate(Blackhole blackhole) {
39+
blackhole.consume(createSchema(SDL));
40+
}
41+
42+
@Benchmark
43+
@BenchmarkMode(Mode.AverageTime)
44+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
45+
public void benchmarkLargeSchemaCreateAvgTime(Blackhole blackhole) {
46+
blackhole.consume(createSchema(SDL));
47+
}
48+
49+
private static GraphQLSchema createSchema(String sdl) {
50+
TypeDefinitionRegistry registry = new SchemaParser().parse(sdl);
51+
return new SchemaGenerator().makeExecutableSchema(registry, RuntimeWiring.MOCKED_WIRING);
52+
}
53+
54+
/* something like
55+
type Query { q : String } interface I { f : String }
56+
interface I1 implements I {
57+
f : String
58+
f1 : String
59+
}
60+
type O1_1 implements I1 & I {
61+
f : String
62+
f1 : String
63+
}
64+
type O1_2 implements I1 & I {
65+
f : String
66+
f1 : String
67+
}
68+
*/
69+
private static String mkSDL() {
70+
int numTypes = 10000;
71+
int numExtends = 10;
72+
73+
StringBuilder sb = new StringBuilder();
74+
sb.append("type Query { q : String } interface I { f : String } interface X { x : String }\n");
75+
for (int i = 0; i < numTypes; i++) {
76+
sb.append("interface I").append(i).append(" implements I { \n")
77+
.append("\tf : String \n")
78+
.append("\tf").append(i).append(" : String \n").append("}\n");
79+
80+
sb.append("type O").append(i).append(" implements I").append(i).append(" & I { \n")
81+
.append("\tf : String \n")
82+
.append("\tf").append(i).append(" : String \n")
83+
.append("}\n");
84+
85+
sb.append("extend type O").append(i).append(" implements X").append(" { \n")
86+
.append("\tx : String \n")
87+
.append("}\n");
88+
89+
for (int j = 0; j < numExtends; j++) {
90+
sb.append("extend type O").append(i).append(" { \n")
91+
.append("\textendedF").append(j).append(" : String \n")
92+
.append("}\n");
93+
94+
}
95+
}
96+
return sb.toString();
97+
}
98+
99+
public static void main(String[] args) throws RunnerException {
100+
try {
101+
runAtStartup();
102+
} catch (Throwable e) {
103+
throw new RuntimeException(e);
104+
}
105+
Options opt = new OptionsBuilder()
106+
.include("benchmark.CreateExtendedSchemaBenchmark")
107+
.build();
108+
109+
new Runner(opt).run();
110+
}
111+
112+
private static void runAtStartup() {
113+
runInToolingForSomeTimeThenExit(
114+
() -> {
115+
},
116+
() -> createSchema(SDL),
117+
() -> {
118+
}
119+
);
120+
}
121+
}

src/main/java/graphql/schema/idl/ArgValueOfAllowedTypeChecker.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,10 @@ private void checkArgValueMatchesAllowedTypeName(List<GraphQLError> errors, Valu
122122
}
123123

124124
String allowedTypeName = ((TypeName) allowedArgType).getName();
125-
TypeDefinition<?> allowedTypeDefinition = typeRegistry.getType(allowedTypeName)
126-
.orElseThrow(() -> new AssertException(format("Directive unknown argument type '%s'. This should have been validated before.", allowedTypeName)));
125+
TypeDefinition<?> allowedTypeDefinition = typeRegistry.getTypeOrNull(allowedTypeName);
126+
if (allowedTypeDefinition == null) {
127+
throw new AssertException(format("Directive unknown argument type '%s'. This should have been validated before.", allowedTypeName));
128+
}
127129

128130
if (allowedTypeDefinition instanceof ScalarTypeDefinition) {
129131
checkArgValueMatchesAllowedScalar(errors, instanceValue, (ScalarTypeDefinition) allowedTypeDefinition);

src/main/java/graphql/schema/idl/ImmutableTypeDefinitionRegistry.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
import graphql.PublicApi;
77
import graphql.language.DirectiveDefinition;
88
import graphql.language.EnumTypeExtensionDefinition;
9+
import graphql.language.ImplementingTypeDefinition;
910
import graphql.language.InputObjectTypeExtensionDefinition;
11+
import graphql.language.InterfaceTypeDefinition;
1012
import graphql.language.InterfaceTypeExtensionDefinition;
13+
import graphql.language.ObjectTypeDefinition;
1114
import graphql.language.ObjectTypeExtensionDefinition;
1215
import graphql.language.SDLDefinition;
1316
import graphql.language.ScalarTypeDefinition;
1417
import graphql.language.ScalarTypeExtensionDefinition;
1518
import graphql.language.SchemaExtensionDefinition;
19+
import graphql.language.Type;
1620
import graphql.language.TypeDefinition;
1721
import graphql.language.UnionTypeExtensionDefinition;
1822
import graphql.schema.idl.errors.SchemaProblem;
@@ -34,6 +38,7 @@
3438
@PublicApi
3539
@NullMarked
3640
public class ImmutableTypeDefinitionRegistry extends TypeDefinitionRegistry {
41+
3742
ImmutableTypeDefinitionRegistry(TypeDefinitionRegistry registry) {
3843
super(
3944
copyOf(registry.objectTypeExtensions),
@@ -51,6 +56,7 @@ public class ImmutableTypeDefinitionRegistry extends TypeDefinitionRegistry {
5156
);
5257
}
5358

59+
5460
private UnsupportedOperationException unsupportedOperationException() {
5561
return new UnsupportedOperationException("The TypeDefinitionRegistry is in read only mode");
5662
}

src/main/java/graphql/schema/idl/ImplementingTypesChecker.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import java.util.HashMap;
3030
import java.util.List;
3131
import java.util.Map;
32-
import java.util.Optional;
32+
import java.util.Objects;
3333
import java.util.Set;
3434
import java.util.function.BinaryOperator;
3535
import java.util.function.Function;
@@ -73,7 +73,7 @@ void checkImplementingTypes(List<GraphQLError> errors, TypeDefinitionRegistry ty
7373
private void checkImplementingType(
7474
List<GraphQLError> errors,
7575
TypeDefinitionRegistry typeRegistry,
76-
ImplementingTypeDefinition type) {
76+
ImplementingTypeDefinition<?> type) {
7777

7878
Map<InterfaceTypeDefinition, ImplementingTypeDefinition> implementedInterfaces =
7979
checkInterfacesNotImplementedMoreThanOnce(errors, type, typeRegistry);
@@ -172,7 +172,7 @@ private void checkInterfaceIsImplemented(
172172

173173
private void checkArgumentConsistency(
174174
String typeOfType,
175-
ImplementingTypeDefinition objectTypeDef,
175+
ImplementingTypeDefinition<?> objectTypeDef,
176176
InterfaceTypeDefinition interfaceTypeDef,
177177
FieldDefinition objectFieldDef,
178178
FieldDefinition interfaceFieldDef,
@@ -211,7 +211,7 @@ private void checkArgumentConsistency(
211211
}
212212

213213
private Map<InterfaceTypeDefinition, List<ImplementingTypeDefinition>> getLogicallyImplementedInterfaces(
214-
ImplementingTypeDefinition type,
214+
ImplementingTypeDefinition<?> type,
215215
TypeDefinitionRegistry typeRegistry
216216
) {
217217

@@ -255,18 +255,17 @@ private <T> BinaryOperator<T> mergeFirstValue() {
255255
return (v1, v2) -> v1;
256256
}
257257

258-
private Optional<InterfaceTypeDefinition> toInterfaceTypeDefinition(Type type, TypeDefinitionRegistry typeRegistry) {
258+
private InterfaceTypeDefinition toInterfaceTypeDefinition(Type<?> type, TypeDefinitionRegistry typeRegistry) {
259259
TypeInfo typeInfo = TypeInfo.typeInfo(type);
260260
TypeName unwrapped = typeInfo.getTypeName();
261261

262-
return typeRegistry.getType(unwrapped, InterfaceTypeDefinition.class);
262+
return typeRegistry.getTypeOrNull(unwrapped, InterfaceTypeDefinition.class);
263263
}
264264

265265
private Set<InterfaceTypeDefinition> toInterfaceTypeDefinitions(TypeDefinitionRegistry typeRegistry, Collection<Type> implementsTypes) {
266266
return implementsTypes.stream()
267267
.map(t -> toInterfaceTypeDefinition(t, typeRegistry))
268-
.filter(Optional::isPresent)
269-
.map(Optional::get)
268+
.filter(Objects::nonNull)
270269
.collect(toSet());
271270
}
272271
}

src/main/java/graphql/schema/idl/SchemaExtensionsChecker.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ static List<OperationTypeDefinition> checkSchemaInvariants(List<GraphQLError> er
7676

7777
// ensure we have a "query" one
7878
Optional<OperationTypeDefinition> query = operationTypeDefinitions.stream().filter(op -> "query".equals(op.getName())).findFirst();
79-
if (!query.isPresent()) {
79+
if (query.isEmpty()) {
8080
// its ok if they have a type named Query
81-
Optional<TypeDefinition> queryType = typeRegistry.getType("Query");
82-
if (!queryType.isPresent()) {
81+
TypeDefinition<?> queryType = typeRegistry.getTypeOrNull("Query");
82+
if (queryType == null) {
8383
errors.add(new QueryOperationMissingError());
8484
}
8585
}
@@ -117,13 +117,13 @@ private static Consumer<OperationTypeDefinition> checkOperationTypesExist(TypeDe
117117
private static Consumer<OperationTypeDefinition> checkOperationTypesAreObjects(TypeDefinitionRegistry typeRegistry, List<GraphQLError> errors) {
118118
return op -> {
119119
// make sure it is defined as a ObjectTypeDef
120-
Type queryType = op.getTypeName();
121-
Optional<TypeDefinition> type = typeRegistry.getType(queryType);
122-
type.ifPresent(typeDef -> {
123-
if (!(typeDef instanceof ObjectTypeDefinition)) {
120+
Type<?> queryType = op.getTypeName();
121+
TypeDefinition<?> type = typeRegistry.getTypeOrNull(queryType);
122+
if (type != null) {
123+
if (!(type instanceof ObjectTypeDefinition)) {
124124
errors.add(new OperationTypesMustBeObjects(op));
125125
}
126-
});
126+
}
127127
};
128128
}
129129

src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,12 @@ public TypeDefinitionRegistry getTypeRegistry() {
141141
}
142142

143143
TypeDefinition<?> getTypeDefinition(Type<?> type) {
144-
Optional<TypeDefinition> optionalTypeDefinition = typeRegistry.getType(type);
145-
146-
return optionalTypeDefinition.orElseThrow(
147-
() -> new AssertException(format(" type definition for type '%s' not found", type))
148-
);
144+
TypeDefinition<?> typeDefinition = typeRegistry.getTypeOrNull(type);
145+
if (typeDefinition != null) {
146+
return typeDefinition;
147+
} else {
148+
throw new AssertException(format(" type definition for type '%s' not found", type));
149+
}
149150
}
150151

151152
boolean stackContains(TypeInfo typeInfo) {
@@ -905,22 +906,21 @@ void buildOperations(BuildContext buildCtx, GraphQLSchema.Builder schemaBuilder)
905906
GraphQLObjectType subscription;
906907

907908
Optional<OperationTypeDefinition> queryOperation = getOperationNamed("query", operationTypeDefs);
908-
if (!queryOperation.isPresent()) {
909-
@SuppressWarnings({"OptionalGetWithoutIsPresent"})
910-
TypeDefinition<?> queryTypeDef = typeRegistry.getType("Query").get();
909+
if (queryOperation.isEmpty()) {
910+
TypeDefinition<?> queryTypeDef = Objects.requireNonNull(typeRegistry.getTypeOrNull("Query"));
911911
query = buildOutputType(buildCtx, TypeName.newTypeName().name(queryTypeDef.getName()).build());
912912
} else {
913913
query = buildOperation(buildCtx, queryOperation.get());
914914
}
915915
schemaBuilder.query(query);
916916

917917
Optional<OperationTypeDefinition> mutationOperation = getOperationNamed("mutation", operationTypeDefs);
918-
if (!mutationOperation.isPresent()) {
919-
if (!typeRegistry.schemaDefinition().isPresent()) {
918+
if (mutationOperation.isEmpty()) {
919+
if (typeRegistry.schemaDefinition().isEmpty()) {
920920
// If no schema definition, then there is no schema keyword. Default to using type called Mutation
921-
Optional<TypeDefinition> mutationTypeDef = typeRegistry.getType("Mutation");
922-
if (mutationTypeDef.isPresent()) {
923-
mutation = buildOutputType(buildCtx, TypeName.newTypeName().name(mutationTypeDef.get().getName()).build());
921+
TypeDefinition<?> mutationTypeDef = typeRegistry.getTypeOrNull("Mutation");
922+
if (mutationTypeDef != null) {
923+
mutation = buildOutputType(buildCtx, TypeName.newTypeName().name(mutationTypeDef.getName()).build());
924924
schemaBuilder.mutation(mutation);
925925
}
926926
}
@@ -930,12 +930,12 @@ void buildOperations(BuildContext buildCtx, GraphQLSchema.Builder schemaBuilder)
930930
}
931931

932932
Optional<OperationTypeDefinition> subscriptionOperation = getOperationNamed("subscription", operationTypeDefs);
933-
if (!subscriptionOperation.isPresent()) {
934-
if (!typeRegistry.schemaDefinition().isPresent()) {
933+
if (subscriptionOperation.isEmpty()) {
934+
if (typeRegistry.schemaDefinition().isEmpty()) {
935935
// If no schema definition, then there is no schema keyword. Default to using type called Subscription
936-
Optional<TypeDefinition> subscriptionTypeDef = typeRegistry.getType("Subscription");
937-
if (subscriptionTypeDef.isPresent()) {
938-
subscription = buildOutputType(buildCtx, TypeName.newTypeName().name(subscriptionTypeDef.get().getName()).build());
936+
TypeDefinition<?> subscriptionTypeDef = typeRegistry.getTypeOrNull("Subscription");
937+
if (subscriptionTypeDef != null) {
938+
subscription = buildOutputType(buildCtx, TypeName.newTypeName().name(subscriptionTypeDef.getName()).build());
939939
schemaBuilder.subscription(subscription);
940940
}
941941
}

src/main/java/graphql/schema/idl/SchemaTypeChecker.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import java.util.LinkedHashSet;
3535
import java.util.List;
3636
import java.util.Map;
37-
import java.util.Optional;
3837
import java.util.Set;
3938
import java.util.function.BiFunction;
4039
import java.util.function.Consumer;
@@ -333,17 +332,19 @@ private void checkFieldTypesPresent(TypeDefinitionRegistry typeRegistry, List<Gr
333332

334333
private Consumer<Type> checkTypeExists(String typeOfType, TypeDefinitionRegistry typeRegistry, List<GraphQLError> errors, TypeDefinition typeDefinition) {
335334
return t -> {
336-
TypeName unwrapped = TypeInfo.typeInfo(t).getTypeName();
337-
if (!typeRegistry.hasType(unwrapped)) {
335+
String name = TypeInfo.typeName(t);
336+
if (!typeRegistry.hasType(name)) {
337+
TypeName unwrapped = TypeInfo.typeInfo(t).getTypeName();
338338
errors.add(new MissingTypeError(typeOfType, typeDefinition, unwrapped));
339339
}
340340
};
341341
}
342342

343343
private Consumer<Type> checkTypeExists(TypeDefinitionRegistry typeRegistry, List<GraphQLError> errors, String typeOfType, Node element, String elementName) {
344344
return ivType -> {
345-
TypeName unwrapped = TypeInfo.typeInfo(ivType).getTypeName();
346-
if (!typeRegistry.hasType(unwrapped)) {
345+
String name = TypeInfo.typeName(ivType);
346+
if (!typeRegistry.hasType(name)) {
347+
TypeName unwrapped = TypeInfo.typeInfo(ivType).getTypeName();
347348
errors.add(new MissingTypeError(typeOfType, element, elementName, unwrapped));
348349
}
349350
};
@@ -353,10 +354,10 @@ private Consumer<? super Type> checkInterfaceTypeExists(TypeDefinitionRegistry t
353354
return t -> {
354355
TypeInfo typeInfo = TypeInfo.typeInfo(t);
355356
TypeName unwrapped = typeInfo.getTypeName();
356-
Optional<TypeDefinition> type = typeRegistry.getType(unwrapped);
357-
if (!type.isPresent()) {
357+
TypeDefinition<?> type = typeRegistry.getTypeOrNull(unwrapped);
358+
if (type == null) {
358359
errors.add(new MissingInterfaceTypeError("interface", typeDefinition, unwrapped));
359-
} else if (!(type.get() instanceof InterfaceTypeDefinition)) {
360+
} else if (!(type instanceof InterfaceTypeDefinition)) {
360361
errors.add(new MissingInterfaceTypeError("interface", typeDefinition, unwrapped));
361362
}
362363
};

0 commit comments

Comments
 (0)