Repository navigation
Speed up resolver method lookup for large schemas - #837
Merged
Merged
Conversation
Schema scanning got very slow for large schemas with many root resolvers. Every root field is searched on every root resolver, and each search recomputed all methods of the resolver class and then made up to five linear passes over them, building the candidate method name again for every method. That made scanning roughly fields x resolvers x methods, so a schema with thousands of root fields took minutes to start. FieldResolverScanner now groups the methods of each class by name once per scan, keyed by class and subscription filter, and looks up the candidate names (name, isName, getName, getFieldName, snake_case variant) directly. The resolution order and argument checks are unchanged. The cache lives on the scanner, so it is discarded with the scan and nothing is shared between schemas. Fixes #541 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #541
Checklist
Description
Schemas with a lot of root fields take minutes to build. The reporter in #541 has 5,611 Query and 16,461 Mutation fields, and finding their resolvers took 84s and 218s.
FieldResolverScanner#findFieldResolvertries every root field on every root resolver. For each of those pairs,findResolverMethodrebuilt the resolver class's full method list (declared, superclass and interface methods) and then made up to five linear passes over it, building the candidate name (name,isName,getName,getFieldName, snake_case variant) again for every method. So the scan was roughly fields x resolvers x methods. JFR on master put ~99% of the scan time infindResolverMethod, mostly list iteration andStringBuilderwork.The scanner now groups each class's methods by name once per scan and looks up the candidate names directly, in the same order as before. The cache is keyed by the class and whether it's the subscription root, which are the only inputs to
getAllMethodsbesides the options.groupBykeeps the original method order within each name, so the method that gets picked, the argument checks and the error messages don't change. The cache lives onFieldResolverScanner, whichSchemaClassScannercreates once perSchemaParserBuilder#scan, so it's thrown away with the scan and nothing is shared between schemas.I timed
SchemaParserBuilder#buildwith generated Java root resolvers. 60 resolvers x 80 fields (4,800 root fields) went from ~9-11s to under 1s. 200 resolvers x 110 fields (22,000 fields, about the size in the issue) went from ~247s to ~2-3s. The new test only checks the lookup order (exact name,isXonly for Boolean fields,getX,getFieldX) since that's the logic I rewrote, so it passes on master too. I didn't add a timing test.I didn't go with the
parallelStreamapproach from the issue. The dictionary, queue and the rest of the stateSchemaClassScannerbuilds up aren't thread-safe, which is most likely where the bogus "can never be accessed" / "no methods on it were used" warnings and the failed startup came from. Scanning is still single-threaded. What's left is fields x root resolvers with a small constant, mostly building the candidate names for each pair. Computing them once per field infindFieldResolverwould probably cut another 2-3x, but I kept this one minimal. The queue phase (~4.4s in the report) isn't touched.Behaviour change: none, apart from scanning being a lot faster for schemas with many root resolvers and fields. A scan now holds one name-indexed method map per resolver class, which is released when it finishes.
🤖 Generated with Claude Code