Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Make GraphQL schema cacheable #7222

Closed
andris-sevcenko opened this issue Dec 4, 2020 · 7 comments
Closed

Make GraphQL schema cacheable #7222

andris-sevcenko opened this issue Dec 4, 2020 · 7 comments
Assignees
Labels
enhancement improvements to existing features graphql ⚙️ features related to the GraphQL API performance 🏃‍♀️ features related to app performance

Comments

@andris-sevcenko
Copy link
Contributor

@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:

Screen Shot 2020-12-03 at 10 35 17 PM

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)

@andris-sevcenko
Copy link
Contributor Author

andris-sevcenko commented Dec 4, 2020

@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.

@andris-sevcenko andris-sevcenko added this to the 4.0 milestone Dec 4, 2020
@andris-sevcenko andris-sevcenko self-assigned this Dec 4, 2020
@andris-sevcenko andris-sevcenko added graphql ⚙️ features related to the GraphQL API performance 🏃‍♀️ features related to app performance labels Dec 4, 2020
@mildlygeeky
Copy link
Contributor

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.

@mildlygeeky
Copy link
Contributor

@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).

@andris-sevcenko
Copy link
Contributor Author

@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.

@andris-sevcenko
Copy link
Contributor Author

In particular, maybe preloading can be made to work here, but that requires PHP 7.4, so the Craft 4.0 milestone still remains.

@andris-sevcenko
Copy link
Contributor Author

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 Gql::EVENT_BEFORE_EXECUTE_GQL_QUERY a plugin could just forward the request to the Swoole server, which would execute the query then and return the results. Obviosuly, this is a rough draft of how that might work, but it's something.

@mildlygeeky
Copy link
Contributor

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.

Friends, this is why I will forever "stan" for @andris-sevcenko. You crack me up, man.

@craftcms craftcms locked and limited conversation to collaborators Jun 22, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
enhancement improvements to existing features graphql ⚙️ features related to the GraphQL API performance 🏃‍♀️ features related to app performance
Projects
None yet
Development

No branches or pull requests

3 participants