-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Make the maximum recycleable parser cache size configurable #1156
Comments
WunderGraph commits fully to Open Source and we want to make sure that we can help you as fast as possible. |
@jfroundjian thanks for raising this, it sounds very reasonable. Do you have any suggestion on what this number could be? |
Thanks @jensneuse setting a default is definitely a good idea I just didn't want to suggest it immediately because it would have some performance implications for those really large requests (which is probably a rare use case). I agree that the number won't have a lot of meaning to folks since it's just a cap on the number of json objects that are parsed in an individual response but it is also difficult to estimate the size since it depends a lot on the JSON being parsed. From my example our subgraph responses can get pretty large so I'll try to do some basic maths here to work backwards:
I think 32,768 or 2^15 is a very reasonable limit that almost all graphql responses will never get to but if you want to be more accomodating going up one more also works. Wdyt? Btw I am going up in powers of 2 because that's how the Parser grows the underlying array to store these values. |
I think your suggestion of 2^15 sounds reasonable. Another case to think about is that we could implement a more sophisticated parser pool. That said, I'm fine going with a simple approach first and seeing where it takes us. |
That sounds pretty nice @jensneuse but going simple first is also a deliberate decision I made because afaict our use case is unique. Btw I also came across this PR that was never merged into fastjson to add a reusability limit for a parser. That's more appropriate for the use case where we occasionally parse large jsons but it's also doing something similar |
Thanks for helping me merge all the PRs and update the documentation @jensneuse. I can confirm that the feature is working as expected in [email protected] and that our high memory usage has been curbed. |
Component(s)
router
Is your feature request related to a problem? Please describe.
Problem:
One of our federated queries is for loading a batch of data >1500 items all with >100 fields. This often results in larger JSON responses that the Resolver ends up parsing. After switching to ParserPools in fastjson (sometime after Router .88.0) we noticed unbounded memory growth when performing a relatively large number of these batch requests (> 150 min) often resulting in unwieldy heap sizes and a struggling GC. This was not a problem for smaller requests (same Query but with <200 items).
It turns out that the issue is for our given queries, the ParserPool ends up retaining Parser objects which have allocated arrays of 2^17 (131,072) astjson.Value objects. This makes the Router retain an unusually large Heap that can not be reduce by the GC because the Parsers are being actively used to resolve GraphQL responses.
I've attached a few pprof dumps from v 0.88.0 and v 0.107.0
v88.pb.gz
v107.pb.gz
Describe the solution you'd like
I'm proposing and have implemented a somewhat simple solution to the problem to discard Parser objects that have unusually large caches so that the GC can eventually release that memory back to the Program.
Additionally, I propose making this a configurable property of the Router and Graphql-go-tools so that the default behavior will continue as is and this value can be set on a per need basis.
I've already tested this locally and it works well for our use case. Here are the 3 PRs I'm proposing:
astjson: Add a PutIfSizeLessThan method to ParserPool
feat: Add configurable size limit to recycled Parsers in pool astjson#2
graphql-go-tools: Add ResolverOption for MaxRecyclableParserSize and use on Resolvable.Reset()
feat: Add configurable size limit for parsers in pool graphql-go-tools#881
router: Add ResolverMaxRecyclableParserSize configuration and pass to Resolver on creation
feat: make the maximum recycleable parser cache size configurable #1157
Describe alternatives you've considered
No response
Additional context
No response
The text was updated successfully, but these errors were encountered: