My GraphQL Performance Problem
HTML-код
- Опубликовано: 10 сен 2024
- I ran into a performance problem related to GraphQL. I go through how I debugged and fixed it.
Links from video:
/ 1212392789637521410
news.ycombinat...
----
Follow me online: voidpet.com/be...
#benawad
Thanks for sharing, this type of content is extremely useful. Not many people share the process of figuring out a problem, very helpful. 10/10 would watch again.
tl;dw 10:40 overfetching fields & 11:12 type-graphql performance, optimized by lowering results fetched and using beta branch of type-graphql
TL;DW GraphQL
I find these types of videos where you troubleshoot actual problems to be incredibly enlightening. I don't think there's enough of this type of content. Bravo! I didn't quite understand the connection to type-graphql though TBH and would love it if you explained that a little more. Really appreciate following along with your thought processes though.
I'm using type-graphql to create my graphql schema, so one thing it does is wrap all my resolvers in some type-graphql code. That code apparently has some problems
Points to one advantage of using Hasura, written in Haskel, and uses a meta-data engine that pre "compiles" types and queries, maps to efficient Postgresql queries...minimizing overhead/redundancy of both layers.
Didn't even know they did that under the hood.
Maybe it's time to reconsider using Hasura instead of Prisma.
Or use diesel & actix with rust. To make your API goo BRRRRRRRR
Fantastic video. Thank you so much for showing step by step your debugging process of a problem. I always learn a ton when I watch someone else try to solve a problem!
Hey Ben, great video. Personally I think that one of performance issues is certainly the overfetching. You shoud fetch only data needed for search component, not for recipe detail. Excited to see performance difference while not overfetching :)
yeah that was certainly part of the problem
@@bawad any way to lazy load the query for the recipe detail after the initial fetch for the search component?
@@cklee_ yeah that's what I'm planning on doing
What I did for one of my sites is launch the request to the load the data for a search result on over not on click, so that by the time the user clicks it has already been loaded and will be instant. That should keep the instant loads of recipes like shown here while reducing the graphql overhead
Glad you figured it out. Facing a similar issue, our problem is that it is only occasionally slow without any perticular query seemingly causing it. This causes CPU to spike in our pods which again causes timeouts etc.
Great content. Please continue showing problem solving processes like this. I think it's more valuable than straight tutorials.
Ah, and can you post in Twitter about what results you got once you pushed to production? (if not another video)? :)
will do
Thanks Ben for explaining and sharing the problem.
I think this is a very much library specific issue. GraphQL is a specification, not an implementation. There are lots of implementation libraries for it that target multiple programming languages and platforms. So, it would be more meaningful to say that this or that implementation is slow, not graphQL as a whole.
We, for example, use graphQL mostly in java (the library is called graphql-java-tools) and we did not measure any significant overhead... 🙃
Your solution for nodejs was great, though. We should inspect catefully our runtime implementations. Thanks for the video!
agreed
really impressive performance-tuning
i learned a lot
high-quality content as always
Probably a problem related to overuse of promises, every promise adds a lot of over head and the garbage collector has to remove all these allocated promises, every field in your type graphql schema is probably implemented as an async function
yep I think it is related to that
Okay but what is the solution then, in nodejs since it is single threaded everything has to be async
one solution is to remove promises where stuff should just be sync
Yes, makes sense. But don't you think that those cases are already (or should) be optimized automatically by the js engine? I'm no google engineer and I think I'd know how to do it...
I am very interested in this because this can basically determine whether it is possible to make a truly performant graphql api. If it turns out that it is not, graphql will be delegated to edge cases such as specific public facing apis and apis for mobile devices or network constrained environments. I really like graphql and would like this not to be the case though.
curious on if you messed with a Graph database like Neo4j?
This is why I started using GoLang. The performance is mind blowing. Other static typed close to C languages are also good.
No, I think the error is that GraphQL resolves the problems that http 1.1 had. That's why serverless options works better, because they work over http 2 or 3.
I just can't stand looking at Go code.. A functional combination of C and Python, but with terrible type support..
If you're really looking for a mind blowing language, master Rust instead.
@@uziboozy4540 Sure. Rust is great as well. That is why, I mentioned any "Other static typed close to C" language.
I don't understand what you do yet, but I will. That's why I've subscribed. I'm just exposing myself to your videos and some things are coming by osmosis.
Until the end of the year I'll probably understand that. Thanks for the video, anyways.
if you're new to GraphQL, I recommend starting with graphql.org/learn/ and ruclips.net/p/PLN3n1USn4xln0j_NN9k4j5hS1thsGibKi
Hi Ben. What version of type-graphql did you need to upgrade too?
0.18.0-beta.8
edit: 0.18.0-beta.9 just came out with even more improvements
This is the type of content missing from the web. Continue with such videos
Extremely useful content Ben.
Thank you for sharing. ❤️
Please take a look at our Project Based React tutorials as well.
Keep doing the great work 👍
Any particular kind of project you had in mind?
Hey @@bawad, yes we do have a bunch of project ideas for tutorials specifically. Which will help people get up and running with React and also newer tech such Apollo which you're very good at. So keep doing the great work and we will keep moving forward as well.
oh lmao this is an advertisement, I thought you were wanting to see me make more project based react tutorials
Hey Ben, it would be interesting to see the performance difference between the regular GraphQL library and Type-GraphQL. That would show whether it really is the culprit of performance issues in your implementation.
yeah this would be good to do, I'm just too lazy to set it up in graphql-js
here's a benchmark though github.com/MichalLytek/type-graphql/pull/488#issuecomment-564592663
how do you set up graph ql with the typescript complier ! Note I don't want to use type Graph QL i just want to use typescript as my compiler
We have decided to use web-sockets on top of AWS API gateway. native, realtime, cross-browser support and pretty type-safe. We had multiple perf issues and caching issues on previous GraphQL project so this time we use good old REST and pure web-sockets.
BFF (Back End For Frontend) pattern says hi 🙋
Great video thanks for showing us how to debug GraphQL requests
Can someone recommend a project based course to learn javascript (+ ecosystem) ? Took one udemy course but abandoned it as it dragged too much
Was this really the solution or did you take any further steps after?
This is a great breakdown video. How were you able to get the query field breakdown to DataDog?
check out datadog APM they have a node.js lib
3:51 you mention that you're using apollo tracing, how did you make it give you that diagram?
That's Apollo platform
For efficient searching, especially considering you have a hierarchy of data that can be searched for, it would be more efficient to use something like Elasticsearch, Solr or Algolia (as a service). GraphQL isn't ideal for free form search, as you will have to do all the optimization yourself, and even then, GraphQL has the overhead it has to process the data before returning it.
Now, this only makes sense as your app grows, so there's no need to be on the edge on it, but just wanted to pitch in!
Wouldn't I still go through GraphQL to query Elasticsearch or do you recommend directly talking to elastic?
@@bawad As with authentication services, it is best to have separate endpoints for those types of services.
I recommend that you create an endpoint (or separate service with its own endpoint, whichever you prefer) to handle requests for search which then in turn uses the search mechanism of your choosing (e.g. Elastic).
how did you test how fast you could md5 hash something?
console.startTime('md5')
imgix.sign()
console.endTime('md5')
It would be nice to see some more performance comparisons.
The same queries with the current and beta type-graphql version, with and without overfetching and with both optimizations.
Don't overfetch, save the planet
Ben can you help how to take a screenshot in react and append it as a file input automatically?
sorry, not sure how to do that
Debugging is tiresome and frustrating :(
To me what jumps right away is the same as you said: the huge garbage collection. It still took one fifth of the total time from what you showed us.
To debug it, first i would only use one request (the one that is causing the performance problem) and not so many of them. And i also wouldn't take out anything form it, including the postgress queries. This to avoid some others non-linear problems that can obscure the data. Then only after that one can begin to add or cut thing as needed.
The main thing to check in those situations is if the bug comes from our code or from some other modules.
What i would do is go up from there to see in which field resolve it originated (since it is a graphql API). Probably checking the 3 "(anonymous)" calls above the 'applyMiddleWares' and see if the resolve functions are from the same fields. Then you can try and dig dipper into them with some test code or just logging the timestamps and number of time they are called and also using normal breakpoints if needed.
The garbage collection seem to be called by tslib.js which is supposed to 'manage' the typescript helpers. Probably a sign of some problem in the decorators or something related? Async await may be involved. Does it needs a try catch for some reason? Or some polyfill that is not behaving correctly with your javascript version and your node version?
Or it could simply be that you only need a simple if statement to check if some value exist?
Or some graphql types/fields may have some circular dependency that is not dealt appropriately?
Debugging is no doubt the worst part of programming. Fortunately at the end of the video, you presented a possible solution. Who knows if it is the right one, but it works and that is what helps preserve the sanity of the normal everyday programmer.
Hey Ben can you try Wundergraph?
when you mentioned md5 and request count, i was sure not this, cause md5 is made to be fast, which is also a reason why its unsafe to hash passwords with it, like many did and do (bruteforce is easier because less time needed to create hash).
17ms on simple description: string default resolver sounds heavy. I bet the default resolver on the library is poorly optimized (or was).
Is there any way in the library to set own default resolver?
idk if the tracing is getting fooled some how, but the default resolver should do nothing
I think it is waiting for other promises to resolve, it isn't using 17ms of cpu time probably. It's a recurring issue I find with js devtools. It should indicate which promise (or promises) it is awaiting
Why not just cache each recipe? It's okay if it takes 500ms, cause most of the time it would be instant
for the foreach callback are you setting it to async?
I think the forEach is something the internals of graphql does
@@bawad ok this link explains what could be the problem with the foreach riptutorial.com/javascript/example/7746/looping-with-async-await
I'm not sure what the code looks like, but you can still use forEach and avoid that
const promises = [];
arr.forEach(item => {
const p = asyncTask(item)
promises.push(p)
});
const results = await Promise.all(promises)
@@bawad or you can do like that, more elegant way : const results = await Promise.all(arr.map((item) => asyncTask(item))) 😉
actually if type checking is such an issue, why type check? Isn't it commmon nowadays to just strip typescript out and run pure js code?
I think he meant type checking by Graphql, not typescript. Typescript is normally always pre-compiled to pure js. But Graphql is a 'typed API'.
@@estranhokonsta cant you strip typescript entirely during the build process? you lose runtime type checking yes, but you get 2 advantages: fast build time, and faster performance. I just assume type graphql makes heavy use of typescript. What checks do have to be done when doing a read? is type graphql double checking the database columns on a read? "is it really an integer?" that'd be stupid
@@Jossnaz
> cant you strip typescript entirely during the build process?
yes
> is type graphql double checking the database columns on a read? "is it really an integer?"
It's not type-graphql, but graphql checks each field to make sure it's the expected type
@@Jossnaz The truth is that modern dev can be so confusing that it let's one doubt many things. All this because modern apps and tech are complex and too many faceted.
Just in this one project there must be typeOrm, TypeGraphQL (i assume) Typescript, GraphQL, etc And all of them have some relation to types.
TypeORM and TypesGraphQL are related as they use types, classes and decorators of Typescript and are more for ease of development for the programmer (although they no doubt each introduce overhead in the prod code).
GraphQL also use its own typing system completely unrelated to Typescript and heavily influence the prodution code.
And so many other layers of complexity.
But just thinking about creating a modern and minimally feature rich app with just vanilla javascript... Hell will not be on the creation part, as it is not so impossible as that. The real suffering will appear when one has to maintain the code. It is difficult as it is for the ones who have created the project, never mind the ones who comes afterward.
@@estranhokonsta well its like a thing of javascript that everything is so volatile. And if you update one thing, all of the sudden all breaks. It's certainly an anti pattern to use the standard versioning style in the package.json imho. Things break soooo quickly. Upside is that javascript is very innovative it seems... good graphql libraries for php? I've seen one with 3k stars... thats really nothing. The question I was wondering about as well is whether graphql really, really is worth it? do you have soooo many rest endpoints otherwise, really? so many calls? usually a bunch of crud + search thats it. I dont know man. I havent seen the value yet
I don't mean to be rude but this is why you shouldn't use javascript for a production backend. You should have chosen something like spark-jobserver with python. The performance would be much better and you would have spark's data analytics tools.
Solve all your GraphQL performance problems by upgrading to REST APIs.
big brain plays
Can you upload a video to let us know if you find out more or an update happens!?
follow me on twitter.com/benawad I'll keep you posted
Young developers repeat 15 years old architectural problems. Server/Client side logic. Instead of building DTO objects on the server side, they cope with bunch of security and performance problems on the client side. Same with the old Asp .Net. Querying 20 objects must be fast like the hell and i did not mention that the database must be very large cca 100 million records.
Use Golang's GraphQL libraries for server
Thanks I’ll try to update it. I had the same issue and ended up creating a database table that contained preprocessed data. Before that, my resolvers we’re taking 6 seconds to complete.
I wonder if it were better sticking with Apollo but typegraphql and TypeORM api is just too good
yikes 6 seconds is a looooooooooooooooong time
yeah I really like the typegraphql/typeorm api, so I'm hoping typegraphql can make some improvements to its performance
Ben Awad it was crazy because each step on it’s own we’re near instant. But combined 6 seconds. Hopefully it gets better, it’s my fav library when used with typeorm
@@themanlihood how are you annotating fields? Do you use decorators? Can I use 1 class for both my graphql type and typeorm model?
Thats a nicu, my Dude.
It's the apollo tracing... It's hefty and should never be used in production. If you disable that and run the profiler, be amazed.
I'm not using apollo tracing in production, just tried it to test locally
Could you do a video about lunching your platform. I plan to do the same soon and create tips or advice would be great
haven't done much to launch it yet, only product hunt
@@bawad He said lunch, not launch.
a possible solution to avoid such huuuuge queries that arent really needed is to create enriched views. Inserts and deletes will be a bit slower though...
Hi, would it be worthy to try using server side rendering (in the case you are using SPA).
ssr wouldn't change anything in this case
Did you try deduplication?
nope, have you had success with that? At what level do you dedupe?
Deduplication in the request level? Data rows? Or like memoizing with data loaders?
For me watching this conclusion is only one... i should stay away from GraphQL forever as i am working with large tabular data sets mostly.
😻😻😻😻
Best way to solve any GraphQL problem. Don't use GraphQL! Problem solved!
That garbage collection is HUGE. Maybe you should go in and edit the field resolver/applymiddlewares. Make it reuse variables rather then creating a new variable each time.
I would, but I didn't write that code. It's from type-graphql
Surprising you are over fetching as that’s basically what graphql is there to avoid. Otherwise you may as well have used rest. In fact with your apparent problem domain it looks like graphql is probably just adding complexity and latency and thus over engineering.
In this case, I was over fetching on purpose to simplify some logic on the frontend
> Otherwise you may as well have used rest
yeah I've been thinking about that lately
Performance problems? Assembler. (:
😱
GraphQl for small application, my advice to use Rest API or Grpc (Best solution, very fast) for production ..
But why? Huge companies like Facebook, Twitter, Airbnb and Twitch ditched REST for GraphQL.
As mentioned in the Video the problem was TypeGraphQL and transfer of huge amounts of data (which should always be avoided).
pharti77 nobody abandoned REST! REST will be still be there for a long time yet.
@@JohnDoe-ji1zv I also agree with that. I use both.
Another reason not to use graphql
Use apollo-server with js simply no n0nsense