Make GraphQL schema cacheable #8968
Replies: 12 comments 11 replies
-
@mildlygeeky yeah, the schema definition is required for every call, otherwise, it's impossible to validate the incoming query against it. We've tried to cache the resulting schema, but, for now, it's impossible, as GraphQL fields are defined within Closures, to help to resolve circular dependencies. When you try to serialize anything with a Closure in it, PHP throws a hissy fit about it, so there's no way currently to cache generated schema. For Craft 4.0, I hope to add something like https://github.com/opis/closure to help alleviate this, but that's not going to happen for 3.6. |
Beta Was this translation helpful? Give feedback.
-
Aaah - that makes sense around closures being the reason why you can't simply memoize/serialize. Thanks for the explanation, @andris-sevcenko, and hope to see this in a future release. |
Beta Was this translation helpful? Give feedback.
-
@brandonkelly @andris-sevcenko if I could put together a PR for this, might this something that could get into 3.x (3.7)? Really seeing a lot of stress on Craft for sites using heavy use of GQL, and would be happy to look at caching this (thought is it would respect Craft's overall GQL caching, and break the cache for this in the same way). |
Beta Was this translation helpful? Give feedback.
-
@mildlygeeky, I decided to give it a shot and see what I can come up with. So, the problem here was that the Schema on the PHP end is a complex beast. It's not only a GraphQL schema to validate a query against, but it also holds all the knowledge on how to resolve different fields and whatnot. Further, since entries can relate entries, for example, the fields have to be defined as callbacks, otherwise, you end up with infinite loops. Now, to go to an even more fun place, callbacks/closures cannot be serialized, so it's hard to cache without any wizardry. Thankfully, there are excellent libraries out there that can do this for us, and we should assume that it's with decent performance. Maybe not perfect, but probably not too much overhead on top of PHPs serialization goes into wrapping all the closures in classes so they can be serialized. Long story short, I made that happen. Now, let's take a detour. If you're not in the mood for detours and rambles, skip to the end. I don't recall if you were a fan of Game Of Thrones, but we can probably all agree that George RR Martin is a master of plot twists and great stories in general. Did you know he started by writing science fiction stories? I was not aware of this until I went hunting for the science fiction story I recalled that pairs up nicely with this GH issue - it was a story I read a long time ago as part of this book. The story, it turns out, was by George RR Martin, and it was called FTA. If you're not into science fiction, you can read the plot synopsis here. If you are, well, that book has some good stories. Anyway, as you might have guessed, caching is way slower than generating the Schema every time. About 3x slower. I'll look into some more, uhh, "experimental" solutions that might be available on some hosts. |
Beta Was this translation helpful? Give feedback.
-
In particular, maybe preloading can be made to work here, but that requires PHP 7.4, so the Craft 4.0 milestone still remains. |
Beta Was this translation helpful? Give feedback.
-
Brainstormed a little bit with @Wiejeben over Discord about this. Currently, seems like the best (maybe the only) realistic approach here is to leverage Swoole here as a dedicated GraphQL server which holds the Schemas in the memory. Then, using the |
Beta Was this translation helpful? Give feedback.
-
Friends, this is why I will forever "stan" for @andris-sevcenko. You crack me up, man. |
Beta Was this translation helpful? Give feedback.
-
One way to support this without fully building Swoole support into core may be to include a gql.php script in web, and mount that as an endpoint for the craftcms/docker containers with Swoole's official docker image (https://github.com/swoole/docker-swoole/tree/master/examples/03-nginx) - we're going to try this out for a new build, and will report back on how it goes. |
Beta Was this translation helpful? Give feedback.
-
Not sure if this helps, but lighthouse-php, the Laravel package for building GraphQL schemas, does have caching. They seem to have solved this by writing the compiled schema to the filesystem as a PHP file; https://lighthouse-php.com/5/performance/schema-caching.html Might this be a route that could be taken for Craft, too? |
Beta Was this translation helpful? Give feedback.
-
Unless this is now something on the roadmap of Craft, I feel like this question has actually gone unanswered. |
Beta Was this translation helpful? Give feedback.
-
We're seeing this issue as well under peak loads, is this still being considered for improvement? |
Beta Was this translation helpful? Give feedback.
-
@andris-sevcenko did you ever manage to get anything working for caching the schema definition via |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
@brandonkelly - So this is interesting - we have a headless site paired with a static site generator, and whenever the site regenerates we are seeing the server spend almost all of its time rebuilding the schema via
Gql::getSchemaDef
- here is some profiling we did on New Relic to see where we were spending so much time - as you can see, a ton of time is being spent in PHP, and actually very little in MySQL:We have our rebuild pretty well-optimized (GQL caching as well as GQL batching, on pages with multiple calls), but digging into the API GQL controller I can see that even with GQL caching, it does quite a bit of work pulling the schema definition on every request. Would it be possible for the schema to be cached once the query has been validated? Seems like there is a lot of time being spent here when it probably doesn't have to.
Originally posted by @mildlygeeky in #6639 (comment)
Beta Was this translation helpful? Give feedback.
All reactions