Video Summary and Transcription
The talk at the GraphQL Galaxy conference delves into NativeGraphQL, a method where a GraphQL query translates directly into one FQL query. This one-to-one translation eliminates the N+1 problem, which often requires batching and caching. NativeGraphQL offers live data, consistency without extra work, and no memory constraints. It just works out of the box and is particularly useful for tree traversal and graph-like problems, unlike traditional joins. The VANA approach leverages FQL for its flexibility and GraphQL for ease of use, ensuring multi-region scalability, 100% ACID compliance, and transactionality. The speaker highlights that not every database uses NativeGraphQL due to the complexity of traditional query languages like SQL. Try NativeGraphQL at VANA for efficient and scalable database interactions.
1. Introduction to NativeGraphQL at VANA
Hi everyone, I'm Brichts, I'm super excited today to talk at the GraphQL Galaxy conference. Today I'm going to talk about NativeGraphQL or GraphQL as a database query language. NativeGraphQL means that a GraphQL query is going to translate into one FQL query. This one-to-one translation has huge advantages. Let's take a detour and understand how GraphQL resolvers work. GraphQL resolvers work by mapping fields to functions. However, this approach can lead to the n plus one problem, where multiple database calls are made. To solve this problem, batching and caching techniques can be used. But these solutions introduce complexity and have limitations. The VANA approach to NativeGraphQL avoids these problems and provides several advantages.
Hi everyone, I'm Brichts, I'm super excited today to talk at the GraphQL Galaxy conference. I'm Databrichts on Twitter, I work for the Vana database and today I'm going to talk about NativeGraphQL or GraphQL as a database query language.
Now if we talk about Native GraphQL at VANA what does it mean? Well, first of all, we have a VANA query language which we call FQL and basically NativeGraphQL means that a GraphQL query is going to translate into one FQL query. That one-to-one translation has huge advantages, so first of all, you might wonder which advantages we'll look into that, and question 2, why doesn't everyone do this if there are such advantages?
To answer these questions, we actually have to answer other questions like how do GraphQL resolvers work? So let's take a detour. How do GraphQL resolvers work? Well, typically if you have a query like this with getList, toDo, title, every field in here, like getList and toDo's and title are fields, will map on a function. So getList will be a function and that will delegate to the toDo's function, that will delegate again to the title function, for example, to the title attribute. This is a resolver chain, which is a chain of functions, but it's actually more of a resolver tree of function calls.
Because here there's one function that calls n functions. And if we turn this around, we get n plus 1 and it's basically a problem. And this is actually called the n plus one problem. That's why I turned it around. And when is this a problem? Well, basically, if you're going to call the database for each of these resolvers, because then you get n plus one database calls, which is not efficient. So question four, how can we solve the n plus one problem? Well, there are multiple solutions. Solution one is batching or n in-memory caching. So in that approach, we're going to hook into these functions, for example, todo.titles, and just wait until all the todo.titles are called and then combine these. So instead of going to do n calls for these todo.titles, we're going to do one call. So in total, two calls. That's batching and that's often combined with caching. So if a similar call comes in, then instead of going to the database, we can go to an in-memory cache, so we don't hit the database at all.
A very popular implementation is Facebook's data loader, which you can just plug in on top of your resolvers and it will handle it for you. However, there's a problem with this solution as well. It should in fact be a last resort. Well, why? Your data is no longer live. It's no longer consistent. You can't apply it on everything. You can't patch everything. So you will have still multiple calls. What about caching validation, memory pressure that you have to do deal with suddenly. So it introduces complexity. So the first question, which advantages that FANAS approach provides? Well, it doesn't deal with these problems because it doesn't have these problems.
2. Advantages and Fit of FQL in Native GraphQL
It is live by default. It is consistent. It requires no extra work. And there is no memory constraint problem. It just works out of the box. So you don't have to do this. The problem here is that what joins solve, which is a join between two tables, is a different problem than what the actual problem is, which is more like a tree traversal problem or a graph-like problem. So joins are maybe the wrong tool for the job. There is an implementation, a very impressive implementation, called JoinMonster, which actually comes from the problem they're trying to solve. A monster join that might be the result of a GraphQL query. That's why FQL actually fits the problem. Because we have the same advantages as the rest of the normal native FQL language, we can actually combine that with FQL and use FQL for the flexibility and power and GraphQL for the ease of use. We have multi-region out-of-the-box, scalability out-of-the-box, we have 100% ACID and transactionality out-of-the-box. So that's what native GraphQL is.
It is live by default. It is consistent. It requires no extra work. And there is no memory constraint problem. It just works out of the box. So you don't have to do this.
Solution two, generate one query, which is what FANAS does behind the scenes. But why doesn't everyone do that? If you would look at SQL, for example, and let's say we would select a star from lists where ID is equal to something, then we would go to the to-do calls and do the same and try to concatenate that query. Of course, we'll have to do it for multiple to-do's, so we'll end up with a join. And the problem is if we go deeper like that in a GraphQL traversal, we might end up with a lot of joins. Now, not only is it super complex to analyze this query and then generate SQL from it and then transform the results back to a GraphQL format, it might also be inefficient depending on the joins. You might overfetch a lot and then have to throw away things. And then how are we going to paginate this? Limit 100 might not be exactly what you're looking for. So the problem here is that what joins solve, which is a join between two tables, is a different problem than what the actual problem is, which is more like a tree traversal problem or a graph-like problem. So joins are maybe the wrong tool for the job.
So there is an implementation, a very impressive implementation, called JoinMonster, which actually comes from the problem they're trying to solve. A monster join that might be the result of a GraphQL query. If you look at the work involved, you can see that it's a complex problem to solve. That's question 4, how can we solve the n plus 1 problem, so the two solutions. That brings us back to question 2, why doesn't everyone do this? Well, we just showed it, the query language might not fit the problem or the execution plan might not fit the problem. Then of course, why does FQL fit the problem? Well, we do it quite differently because it's a different query language and has quite graph-like properties. If we would look at the same query, we would start by getting a list with match, index and the list ID. We would immediately wrap it in paginate, so we actually will have pagination on every level, and very sane pagination with an after and before cursor that is always correct. Then we just map over the results of these lists and we would call a function. That's actually just like a normal programming language, where you would just map over something and then call the function. In that function we can do whatever we want and if we look at the get to dos there, well, what is this? It's just a JavaScript function because I'm using the JavaScript driver for FQL, where we just throw out more FQL. Pure function composition. Then we see the same pattern, paginate and map. So we have the second level of pagination immediately and map and again a function that will be called. This is actually a graph-like reversal that we're just implementing in FQL. Because that's possible, it was super easy for Fana to implement that one-to-one translation from GraphQL to FQL. So what is actually happening here, if we look at the query execution, is that we map get over all the lists, then we paginate that immediately and then we just continue map getting and paginate on every level. There is no monster join problem because we do it completely different, so we don't have to solve the problem. So question five, that's why FQL actually fits the problem. Back to question one, which advantage does that bring, because we have mentioned advantages but there are others. Because we have the same advantages as the rest of the normal native FQL language, we can actually combine that with FQL and use FQL for the flexibility and power and GraphQL for the ease of use. We have multi-region out-of-the-box, scalability out-of-the-box, we have 100% ACID and transactionality out-of-the-box. So that's what native GraphQL is.
Comments