5 ms·
I'm not sure if this is mentioned in the thread, but one of the reason it takes so long for the requests to return is when GQL initializes the entire record in
by picardo 7y ago
I'm not sure if this is mentioned in the thread, but one of the reason it takes so long for the requests to return is when GQL initializes the entire record in memory and then reduces it back to only the fields you wanted. This can be a big problem if you have a deeply nested data model, and potentially many results. The memory consumption can hit the roof. I find that the best approach in those cases is to create a one-off REST endpoint (or to create a field higher up the GQL hierarchy) and handroll the SQL query.
- np_tedious 7y agoHow deeply do you mean "entire record"? I am pretty sure that this concern only applies to those fields which have the same resolver
- greenpizza13 7y agoThings have matured quite a bit. With Apollo Server it's possible to fully understand which fields are being requested before creating and running, for example, and SQL query. Fetching only the requested data for a given query reduces in-memory footprint. Most people get the whole data object and then allow GQL to select the subset of fields the user asked for, but for cases where performance is a problem there is another solution.
- andrewem 7y agoYou say "problem there is another solution" - what is the other solution? (I'm guessing it involves somehow telling Apollo Server which fields/related objects you will need?)
- picardo 7y agoI haven't used Apollo Server lately. But the way you describe it doesn't address the core issue, which is the initialization of the intermediate objects in-memory. So just to give an example, if I wanted to query for the projects of listings of my company, I can write it this way in GQL: me { company { listings { projects { id name } } } } This will initialize: a User, a Company, Listings and Projects of all listings. I can also write this in SQL using a couple of joins and return an array. The memory consumption is trivial in comparison to the original request.
- klarstrup 7y agoYou can implement your GraphQL server to do either of those, it's not inherent.
- viraptor 7y ago> when GQL initializes the entire record in memory GQL is an idea not an implementation. I don't believe there's anything preventing actual software from optimising this case. Or am I missing something here? The query defines what you're asking for so extra data does not necessarily need to be fetched.
- xtagon 7y agoYou're correct. It's common for GraphQL API implementations to batch all the parent record's fields up front, but it's not the only way. One alternative method is to traverse the whole query object and generate one big query to your database (SQL, graph database, what have you) instead of batching queries per table/object. This has trade-offs. Sometimes it's more performant, especially for smaller queries, but for larger queries it can actually be slower because joining lots of tables into one query causes some duplicated data and transfer overhead (assuming you're using SQL). I have a feeling that this method would perform very well if your GraphQL data was backed by an actual graph database though.
- city41 7y agoIt's also possible to look at what fields were requested in the GraphQL query and use them to aid what gets fetched.
- benawad 7y agoI thought it could have been a memory problem too, but VPS didn't show any signs of anything spiking https://twitter.com/benawad/status/1212404379371917313 https://twitter.com/benawad/status/1212404379371917313 but I do think it's related to my nested object https://twitter.com/benawad/status/1212407236284338176 https://twitter.com/benawad/status/1212407236284338176
- sergiotapia 7y agoI'm positive there's a way to only load the relevant data in your stack. In Elixir with Absinthe we can resolve to the specific fields we need and we don't load the entire records then slim down.
- picardo 7y agoI never used Absinthe, but if you're initializing an ORM in your resolver, loading the entire record into memory is unavoidable. How does Absinthe get around that? (Sounds like it generates the SQL?)
- sergiotapia 7y agoyou point it to a resolver, in your resolver your resolve, in other words write the query
- nivertech 7y agoAbsinthe does some dumb things by default too. You need to manually optimize your resolvers. One annoying thing Absinthe did by default I noticed was fetching entire object from DB, even though the GraphQL query only returns it's ID. For example query below would fetch each person from a DB, even though we already had a list of person IDs on the friends level: { friends { person { id } } }