Martin Schulze created SLING-13302:
--------------------------------------

             Summary: [GraphQL] Cache executable GraphQLSchema in 
DefaultQueryExecutor to avoid OOM under concurrent load
                 Key: SLING-13302
                 URL: https://issues.apache.org/jira/browse/SLING-13302
             Project: Sling
          Issue Type: Bug
          Components: GraphQL
            Reporter: Martin Schulze


## Problem

DefaultQueryExecutor already caches the parsed TypeDefinitionRegistry (keyed by 
SDL hash, bounded by schemaCacheSize), but still calls makeExecutableSchema / 
RuntimeWiring on every execute/validate.

For large schemas this is expensive in CPU and heap. Under concurrent load many 
Jetty threads rebuild the same executable GraphQLSchema in parallel and can OOM 
("death by a thousand cuts").

Historically the executable schema was left uncached on purpose: RuntimeWiring 
closed over the request Resource, so a shared GraphQLSchema would pin 
request-scoped resources across requests (see SLING-10085 / schema cache 
comments).

## Proposed change

1. Put the request Resource into GraphQLContext and resolve it (and live OSGi 
DataFetcher / TypeResolver services) at fetch/resolve time, so wiring no longer 
embeds a request Resource.
2. Optionally cache GraphQLSchema by SDL hash, with per-key single-flight 
(ConcurrentHashMap + CompletableFuture) so concurrent misses share one build.
3. Gate the new cache with an OSGi metatype flag (e.g. 
executableSchemaCacheEnabled, default false) so behaviour can be rolled out / 
rolled back without a code change. Reuse schemaCacheSize as the LRU bound.

No Caffeine/ehcache dependency — stay on JDK collections, consistent with the 
existing registry LRU.

## Invalidation

Same content-addressed model as the registry cache: key = SHA-256(SDL). New SDL 
→ new hash → miss. Optional clear on component reactivation.

## Related

Adobe SITES-48793 / production OOM under GraphQL load with large CF model 
schemas.

## Testing

- Unit tests: cache hit reuses same GraphQLSchema instance; disabled path 
builds independently; concurrent misses coalesce to a single build.
- Existing DefaultQueryExecutor / IT coverage for Resource-backed fetchers 
still passes with Resource supplied via GraphQLContext.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to