SLING-10085 - Cache the GraphQL schemas in the DefaultQueryExecutor
* cache the TypeRegistryDefinition, based on the schema's text representation;
the GraphQL schema object cannot be cached, due to its embedded runtime
wiring, which holds a Resource object
diff --git a/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java b/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
index 063f3ff..3591bca 100644
--- a/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
+++ b/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
@@ -97,10 +97,11 @@
private static final LogSanitizer cleanLog = new LogSanitizer();
private Map<String, String> resourceToHashMap;
- private Map<String, GraphQLSchema> hashToSchemaMap;
+ private Map<String, TypeDefinitionRegistry> hashToSchemaMap;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
+ private final SchemaGenerator schemaGenerator = new SchemaGenerator();
@Reference
private RankedSchemaProviders schemaProvider;
@@ -146,7 +147,8 @@
Arrays.toString(selectors)));
}
LOGGER.debug("Resource {} maps to GQL schema {}", queryResource.getPath(), schemaDef);
- final GraphQLSchema schema = getSchema(schemaDef, queryResource, selectors);
+ final TypeDefinitionRegistry typeDefinitionRegistry = getTypeDefinitionRegistry(schemaDef, queryResource, selectors);
+ final GraphQLSchema schema = buildSchema(typeDefinitionRegistry, queryResource);
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query(query)
.variables(variables)
@@ -182,7 +184,8 @@
Arrays.toString(selectors)));
}
LOGGER.debug("Resource {} maps to GQL schema {}", queryResource.getPath(), schemaDef);
- final GraphQLSchema schema = getSchema(schemaDef, queryResource, selectors);
+ final TypeDefinitionRegistry typeDefinitionRegistry = getTypeDefinitionRegistry(schemaDef, queryResource, selectors);
+ final GraphQLSchema schema = buildSchema(typeDefinitionRegistry, queryResource);
final GraphQL graphQL = GraphQL.newGraphQL(schema).build();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Executing query\n[{}]\nat [{}] with variables [{}]",
@@ -332,7 +335,7 @@
}
}
- GraphQLSchema getSchema(@NotNull String sdl, @NotNull Resource currentResource, @NotNull String[] selectors) {
+ TypeDefinitionRegistry getTypeDefinitionRegistry(@NotNull String sdl, @NotNull Resource currentResource, @NotNull String[] selectors) {
readLock.lock();
String newHash = SHA256Hasher.getHash(sdl);
/*
@@ -352,12 +355,8 @@
if (!newHash.equals(oldHash)) {
resourceToHashMap.put(resourceToHashMapKey, newHash);
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
- Iterable<GraphQLScalarType> scalars = scalarsProvider.getCustomScalars(typeRegistry.scalars());
- RuntimeWiring runtimeWiring = buildWiring(typeRegistry, scalars, currentResource);
- SchemaGenerator schemaGenerator = new SchemaGenerator();
- GraphQLSchema schema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
- hashToSchemaMap.put(newHash, schema);
- return schema;
+ hashToSchemaMap.put(newHash, typeRegistry);
+ return typeRegistry;
}
readLock.lock();
} finally {
@@ -371,6 +370,12 @@
}
}
+ private GraphQLSchema buildSchema(@NotNull TypeDefinitionRegistry typeRegistry, @NotNull Resource currentResource) {
+ Iterable<GraphQLScalarType> scalars = scalarsProvider.getCustomScalars(typeRegistry.scalars());
+ RuntimeWiring runtimeWiring = buildWiring(typeRegistry, scalars, currentResource);
+ return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
+ }
+
private String getCacheKey(@NotNull Resource resource, @NotNull String[] selectors) {
return resource.getPath() + ":" + String.join(".", selectors);
}
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java b/src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java
index cbad4f2..769f168 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java
@@ -51,7 +51,7 @@
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
-import graphql.schema.GraphQLSchema;
+import graphql.schema.idl.TypeDefinitionRegistry;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.containsString;
@@ -330,28 +330,33 @@
}
@Test
- public void testCachedSchemas() throws IOException {
+ public void testCachedTypeRegistry() throws IOException {
// by default we'll get the test-schema
final DefaultQueryExecutor queryExecutor = (DefaultQueryExecutor) context.getService(QueryExecutor.class);
final RankedSchemaProviders schemaProvider = context.getService(RankedSchemaProviders.class);
assertNotNull(queryExecutor);
assertNotNull(schemaProvider);
String[] selectors = new String[]{};
- GraphQLSchema schema1 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- GraphQLSchema schema2 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- assertEquals(schema1, schema2);
+ TypeDefinitionRegistry
+ registry1 = queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ TypeDefinitionRegistry registry2 =
+ queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ assertEquals(registry1, registry2);
// change the schema provider
context.registerService(SchemaProvider.class, new MockSchemaProvider("test-schema-selected-foryou"), Constants.SERVICE_RANKING,
Integer.MAX_VALUE);
- GraphQLSchema schema3 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- GraphQLSchema schema4 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- assertEquals(schema3, schema4);
- assertNotEquals(schema1, schema3);
+ TypeDefinitionRegistry
+ registry3 = queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ TypeDefinitionRegistry registry4 =
+ queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ assertEquals(registry1, registry2);
+ assertEquals(registry3, registry4);
+ assertNotEquals(registry1, registry3);
}
@Test
- public void testSchemasWithTheCacheDisabled() throws IOException {
+ public void testTypeRegistryWithTheCacheDisabled() throws IOException {
Map<String, Object> properties = new HashMap<>();
properties.put("schemaCacheSize", 0);
@@ -364,16 +369,20 @@
assertNotNull(queryExecutor);
assertNotNull(schemaProvider);
String[] selectors = new String[]{};
- GraphQLSchema schema1 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- GraphQLSchema schema2 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- assertNotEquals(schema1, schema2);
+ TypeDefinitionRegistry
+ registry1 = queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ TypeDefinitionRegistry registry2 =
+ queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ assertNotEquals(registry1, registry2);
// change the schema provider
context.registerService(SchemaProvider.class, new MockSchemaProvider("test-schema-selected-foryou"), Constants.SERVICE_RANKING,
Integer.MAX_VALUE);
- GraphQLSchema schema3 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- GraphQLSchema schema4 = queryExecutor.getSchema(schemaProvider.getSchema(resource, selectors), resource, selectors);
- assertNotEquals(schema3, schema4);
+ TypeDefinitionRegistry
+ registry3 = queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ TypeDefinitionRegistry registry4 =
+ queryExecutor.getTypeDefinitionRegistry(schemaProvider.getSchema(resource, selectors), resource, selectors);
+ assertNotEquals(registry3, registry4);
}
}