Replies: 5 comments
-
Hi, Thanks for bringing this up! I've wanted to eventually investigate the dataloader pattern, so I'm very happy to see that you have already been able to work with it in Vendure. I've not done anything on it so far since we weren't quite there on the sequence of "make it work, make it right, make it fast". But now I think its definitely worth looking into how we can use this to make the default Vendure perf faster. I would propose an initial evaluation like this:
Also noteworthy is the potential cost of using request-scoped providers:
|
Beta Was this translation helpful? Give feedback.
-
I would say the following queries are a good start in the Shop API:
To avoid request-scoped providers, you can use something like https://www.npmjs.com/package/nestjs-dataloader. I didn't like declaring the loader type on each usage, but they'll be faster that way. |
Beta Was this translation helpful? Give feedback.
-
Did a POC dataloader implementation here: 858540c Note that this was done after already putting in significant perf work as part of #1506, so the dataloader did not seem to significantly improve on the work already done. Right now I'm going to leave this and possibly revisit in the next round of perf work. |
Beta Was this translation helpful? Give feedback.
-
Hey Michael, Just a few notes for the next time you revisit this: You didn't get any improvements because you're still doing N+1 queries. For each product, you're getting a list of variants (N queries here) and then fetching all variants in bulk which is not faster than the previous approach. All SQL queries should go into the loader:
and the field should look like this:
I haven't run the code, you may need to tweak the query. This can probably wait for the next perf work. |
Beta Was this translation helpful? Give feedback.
-
My experience with writing dataloaders for a newsfeed implementation is that they promote performance improvements if you avoid joining. Especially, if certain referenced data elements are retrieved as lists and are repetitive. E.g. getting all the users and photos related to posts for a newsfeed, getting all the products including photos for a collection. It makes items also more cacheable, because the query result elements become more atomic and less specific to a certain join scenario. You're just getting a root object and then getting related objects in a series of IN [...id] style queries. Of course, the trade off is exactly here, sometimes it's faster to get stuff in one go with a join as opposed to as subsequent queries. So, it depends. And performance has multiple facets, scalability, memory intensity, etc. Atomic queries get executed faster on a per query basis than complicated join style queries. That might become relevant if you're hosting a large market place on Vendure. |
Beta Was this translation helpful? Give feedback.
-
Hi,
Couple of months ago, we've run into an issue with resolving fields from multiple product variants where for each variant and field a separate SQL query was called. This made calls for big pages of product variants slow so we started using data loaders.
The data loader pattern enables the backend to resolve multiple fields for multiple items using a single SQL query. It batches all calls to a particular function. The data is cached over a single request so you could reuse it from other parts of the code.
For a real life example, I'll share one of our smaller loaders:
The resolver which uses this loader looks like this:
Using this approach the GQL query
{ product(id) { id, facetValues { categoryCount, productCount }
will be resolved in 1-2 SQL queries. We've actually added our own schema which resolves faster than the Vendure one:I can provide a POC PR for any of the Vendure entities if you want. We're really hoping that you're going to start using this pattern as it speeds up the resolving tremendously.
Beta Was this translation helpful? Give feedback.
All reactions