Skip to content

Commit 97efeb3

Browse files
authored
Make TypeDefinitionRegistry serializable (#2454)
1 parent 492e318 commit 97efeb3

4 files changed

Lines changed: 174 additions & 3 deletions

File tree

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import graphql.schema.idl.errors.TypeRedefinitionError;
2929
import graphql.util.FpKit;
3030

31+
import java.io.Serializable;
3132
import java.util.ArrayList;
3233
import java.util.Collection;
3334
import java.util.LinkedHashMap;
@@ -47,7 +48,7 @@
4748
* a graphql schema definition file via {@link SchemaParser#parse(String)}
4849
*/
4950
@PublicApi
50-
public class TypeDefinitionRegistry {
51+
public class TypeDefinitionRegistry implements Serializable {
5152

5253
private final Map<String, List<ObjectTypeExtensionDefinition>> objectTypeExtensions = new LinkedHashMap<>();
5354
private final Map<String, List<InterfaceTypeExtensionDefinition>> interfaceTypeExtensions = new LinkedHashMap<>();
@@ -418,7 +419,7 @@ public boolean hasType(TypeName typeName) {
418419
return types.containsKey(name) || ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(name) || scalarTypes.containsKey(name) || objectTypeExtensions.containsKey(name);
419420
}
420421

421-
public Optional<TypeDefinition> getType(Type type) {
422+
public Optional<TypeDefinition> getType(Type type) {
422423
String typeName = TypeInfo.typeInfo(type).getName();
423424
return getType(typeName);
424425
}

src/test/groovy/graphql/schema/idl/TypeDefinitionRegistryTest.groovy

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import graphql.language.ScalarTypeDefinition
1616
import graphql.language.ScalarTypeExtensionDefinition
1717
import graphql.language.SchemaDefinition
1818
import graphql.language.Type
19+
import graphql.language.TypeDefinition
1920
import graphql.language.TypeName
2021
import graphql.language.UnionTypeDefinition
2122
import graphql.language.UnionTypeExtensionDefinition
@@ -26,10 +27,11 @@ import spock.lang.Unroll
2627

2728
class TypeDefinitionRegistryTest extends Specification {
2829

29-
TypeDefinitionRegistry parse(String spec) {
30+
static TypeDefinitionRegistry parse(String spec) {
3031
new SchemaParser().parse(spec)
3132
}
3233

34+
3335
def "test default scalars are locked in"() {
3436

3537
def registry = new TypeDefinitionRegistry()
@@ -931,4 +933,69 @@ class TypeDefinitionRegistryTest extends Specification {
931933
error.isPresent()
932934
error.get().getMessage().contains("tried to redefine existing 'bar' type")
933935
}
936+
937+
def "can be serialized and hence cacheable"() {
938+
def sdl = '''
939+
"the schema"
940+
schema {
941+
query : Q
942+
}
943+
944+
"the query type"
945+
type Q {
946+
field( arg : String! = "default") : FieldType @deprecated(reason : "no good")
947+
}
948+
949+
interface FieldType {
950+
f : UnionType
951+
}
952+
953+
type FieldTypeImpl implements FieldType {
954+
f : UnionType
955+
}
956+
957+
union UnionType = Foo | Bar
958+
959+
type Foo {
960+
foo : String
961+
}
962+
963+
type Bar {
964+
bar : String
965+
}
966+
'''
967+
def registryOut = new SchemaParser().parse(sdl)
968+
969+
when:
970+
971+
TypeDefinitionRegistry registryIn = serialise(registryOut)
972+
973+
then:
974+
TypeDefinition typeIn = registryIn.getType(typeName).get()
975+
TypeDefinition typeOut = registryOut.getType(typeName).get()
976+
typeIn.isEqualTo(typeOut)
977+
978+
where:
979+
typeName | _
980+
"Q" | _
981+
"FieldType" | _
982+
"FieldTypeImpl" | _
983+
"UnionType" | _
984+
"Foo" | _
985+
"Bar" | _
986+
}
987+
988+
static TypeDefinitionRegistry serialise(TypeDefinitionRegistry registryOut) {
989+
ByteArrayOutputStream baOS = new ByteArrayOutputStream()
990+
ObjectOutputStream oos = new ObjectOutputStream(baOS)
991+
992+
oos.writeObject(registryOut)
993+
994+
ByteArrayInputStream baIS = new ByteArrayInputStream(baOS.toByteArray())
995+
ObjectInputStream ois = new ObjectInputStream(baIS)
996+
997+
ois.readObject() as TypeDefinitionRegistry
998+
}
999+
1000+
9341001
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package benchmark;
2+
3+
import com.google.common.io.Files;
4+
5+
import java.io.File;
6+
import java.net.URL;
7+
import java.nio.charset.Charset;
8+
import java.util.concurrent.Callable;
9+
10+
public class BenchmarkUtils {
11+
12+
static String loadResource(String name) {
13+
return asRTE(() -> {
14+
URL resource = BenchmarkUtils.class.getClassLoader().getResource(name);
15+
return String.join("\n", Files.readLines(new File(resource.toURI()), Charset.defaultCharset()));
16+
17+
});
18+
}
19+
20+
static <T> T asRTE(Callable<T> callable) {
21+
try {
22+
return callable.call();
23+
} catch (Exception e) {
24+
throw new RuntimeException(e);
25+
}
26+
}
27+
28+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package benchmark;
2+
3+
import graphql.schema.idl.SchemaParser;
4+
import graphql.schema.idl.TypeDefinitionRegistry;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.openjdk.jmh.annotations.Benchmark;
7+
import org.openjdk.jmh.annotations.BenchmarkMode;
8+
import org.openjdk.jmh.annotations.Measurement;
9+
import org.openjdk.jmh.annotations.Mode;
10+
import org.openjdk.jmh.annotations.OutputTimeUnit;
11+
import org.openjdk.jmh.annotations.Warmup;
12+
import org.openjdk.jmh.infra.Blackhole;
13+
14+
import java.io.ByteArrayInputStream;
15+
import java.io.ByteArrayOutputStream;
16+
import java.io.ObjectInputStream;
17+
import java.io.ObjectOutputStream;
18+
import java.util.concurrent.TimeUnit;
19+
20+
import static benchmark.BenchmarkUtils.asRTE;
21+
22+
/**
23+
* This benchmarks {@link graphql.schema.idl.TypeDefinitionRegistry} parsing and serialisation
24+
* <p>
25+
* See https://github.com/openjdk/jmh/tree/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/ for more samples
26+
* on what you can do with JMH
27+
* <p>
28+
* You MUST have the JMH plugin for IDEA in place for this to work : https://github.com/artyushov/idea-jmh-plugin
29+
* <p>
30+
* Install it and then just hit "Run" on a certain benchmark method
31+
*/
32+
@Warmup(iterations = 2, time = 5, batchSize = 3)
33+
@Measurement(iterations = 3, time = 10, batchSize = 4)
34+
public class TypeDefinitionParserVersusSerializeBenchMark {
35+
36+
static SchemaParser schemaParser = new SchemaParser();
37+
static String SDL = BenchmarkUtils.loadResource("large-schema-2.graphqls");
38+
static TypeDefinitionRegistry registryOut = schemaParser.parse(SDL);
39+
static ByteArrayOutputStream baOS = serialisedRegistryStream(registryOut);
40+
41+
@Benchmark
42+
@BenchmarkMode(Mode.Throughput)
43+
@OutputTimeUnit(TimeUnit.SECONDS)
44+
public void benchMarkParsing(Blackhole blackhole) {
45+
blackhole.consume(schemaParser.parse(SDL));
46+
}
47+
48+
@Benchmark
49+
@BenchmarkMode(Mode.Throughput)
50+
@OutputTimeUnit(TimeUnit.SECONDS)
51+
public void benchMarkSerializing(Blackhole blackhole) {
52+
blackhole.consume(serialise());
53+
}
54+
55+
static TypeDefinitionRegistry serialise() {
56+
return asRTE(() -> {
57+
58+
ByteArrayInputStream baIS = new ByteArrayInputStream(baOS.toByteArray());
59+
ObjectInputStream ois = new ObjectInputStream(baIS);
60+
61+
return (TypeDefinitionRegistry) ois.readObject();
62+
});
63+
}
64+
65+
@NotNull
66+
private static ByteArrayOutputStream serialisedRegistryStream(TypeDefinitionRegistry registryOut) {
67+
return asRTE(() -> {
68+
ByteArrayOutputStream baOS = new ByteArrayOutputStream();
69+
ObjectOutputStream oos = new ObjectOutputStream(baOS);
70+
71+
oos.writeObject(registryOut);
72+
return baOS;
73+
});
74+
}
75+
}

0 commit comments

Comments
 (0)