Repository navigation
Conversation
ValuesResolverOneOfValidation walks the full input object tree of every argument on every argument coercion and every variable coercion, and allocates a stream pipeline plus a collected list per input object field, solely to find @OneOf types and enforce their single-key constraint. On schemas that use no @OneOf types this walk can never do anything but still runs for every input argument of every field of every request. Memoize per GraphQLInputObjectType whether its transitively reachable input types include a @OneOf type, and skip the walk when none is reachable. The check is cycle-safe for self-referencing input types. When a @OneOf type is reachable the existing walk runs unchanged. Synthetic execution benchmark (5.5k fetched fields/op, 501 fields/op carrying nested input-object arguments, ThreadMXBean allocated bytes): -3.6% bytes/op with default instrumentation, -1.6% bytes/op with 8 chained instrumentations. Generated by Josh-Bot 9000
| * Per input object type: true if the type itself or any transitively reachable input object type is a | ||
| * @oneOf type. Input types are immutable once built, so this answer is stable for a given type instance. | ||
| */ | ||
| private static final Map<GraphQLInputObjectType, Boolean> CONTAINS_ONE_OF = new ConcurrentHashMap<>(); |
There was a problem hiding this comment.
While the idea here has merit I think the static is the wrong place to cache the object types.
A graphql server MAY use many different schemas over its life time. However map is cached JVM wide. So its every growing.
I think this question (does an input type have the @OneOf directive would be better cached inside the schema
schema.inputTypeContainsOneOf( inputType)
This could then be computed at schema build time and cached in a map that does not change.
That way if the schema goes away - the knowledge of the types goes with it.
As it stands today if the schema goes away, the static map here conitnues to have a reference to the object type and hence the schema.
So the schema can never be garbage collected and schemas are very large objects
There was a problem hiding this comment.
Ahh just saw
If that is a concern I can switch to weak keys or compute the flag at schema build time instead — happy to take guidance on the preferred form.
There was a problem hiding this comment.
I understand the concerns @bbakerman, I'm still running through some performance tests on my side before I move this out of draft at which point I'll also address the concerns about a static cache
bbakerman
left a comment
There was a problem hiding this comment.
This needs to cache to be inside the schema object - not in a JVM static
Test ReportTest Results
Code Coverage (Java 25)
Changed Class Coverage (1 class)
ValuesResolverOneOfValidation — method details
|
Compute immutable input-type reachability during schema construction and use it for schema-aware coercion. Remove the JVM-wide cache so discarded schemas do not leave their input types retained. Preserve the original validation path for schema-less calls and detached types. Cover recursive and wrapped inputs, both builders, schema transforms, and resolver fallbacks.
Mark optional schema metadata explicitly nullable and keep the conversion overload private to its class. Group argument resolver overloads together and use the existing test import convention.
Parameterize absent, unrelated, and reachable OneOf types across recursive input graphs. Separate cached-document execution, variable coercion, and schema construction, with parsing outside the measured paths. Use existing execution APIs so the benchmark can run on both sides of the optimization, and check that the fixtures exercise the intended inputs.
ValuesResolverOneOfValidationrecursively inspects coerced input values even when their types cannot reach an@oneOfinput object. Schema-aware argument and variable coercion can skip that dedicated walk for those types.The schema now owns an immutable reachability map, computed during schema construction. Reverse traversal from OneOf types handles recursive input graphs and unresolved type references. Type-changing builds recompute the map;
transformWithoutTypesshares it. Both the standard builder andFastBuilderare covered. There is no JVM-wide cache that retains discarded schema types.Existing resolver signatures remain available. Schema-less calls and detached input types retain the original validation walk. Code registry field visibility and the existing OneOf error behavior remain unchanged.
The JMH benchmark separates cached-document execution, direct variable coercion, and schema construction. Its parameters select absent, unrelated, or reachable OneOf types and recursive input graphs of depth 1 or 8.