mrichar1 / pinia-jsonapi

Use a JSONAPI api with a Pinia store, with data restructuring/normalization. Topics
GNU Affero General Public License v3.0
8 stars 2 forks source link

Performance issue when used in conjonction with vuetify virtual-scroller #2

Open Bartheleway opened 4 months ago

Bartheleway commented 4 months ago

Hi, I'm opening this thread as I discovered that this package seems responsible of some huge performance issues I'm facing with my app.

I basically workaround this issue by creating a computed value based on a list of value coming from this lib and generating a simplified version of it.

If I use directly records in a v-autocomplete (it uses a virtual-scroller behind the scene), I can see my CPU going crazy, page freezes and can take up to 5min to display the 10 items. If I use the computed value, display is taking some milliseconds.

Do you have any idea why this is happening? Does other people have the same issue?

const recordsForDisplay = computed(() => records.value.map(record=> ({
    id: record._jv.id,
    name: record.name,
})))

// knowing that
const records = ref([])

records.value = await dataStore.search([
    'mytype',
    {
        params: {
            sort: 'name',
            page: {
                limit: 10,
            }
        }
    },
])

P.S: I also opened an issue on Vuetify side as I was first thinking issue was on their side. I believe issue is more on this library now.

mrichar1 commented 4 months ago

Hi - thanks for opening this issue.

The usual cause of performance issues is that the normalised data returned by pinia-jsonapi can be recursive, or massively branching, meaning that any code that walks the returned data may continue forever or use excess memory trying to build an object representation. There are some places in the code where we try to prevent recursion, but this is not always possible.

The easy way to check this is to either use console.log to examine the data and look for recursion, or use another 'walking' library (like JSON.stringify) and see if the same issue occurs.

Equally, if you are only interested in id and name and don't need any of the relationships mapped out, then setting the config option followRelationshipsData: false should remove opportunities for recursion.

Let us know if this fixes the issue - otherwise if you can share the contents of records we can have another look to see if there are any obvious issues there.

Bartheleway commented 3 months ago

Hi, thanks for this explaination. While it does make sense, I don't really get why I am getting such performance issues when I am not following the relationships.

Does the lib follow all relationships even if we don't call them in the page?

mrichar1 commented 3 months ago

The issue is not that pinia-jsonapi follows relationships, but that other libraries which are passed the normalised object may do so - the commonest is JSON.stringify but anything which attempts to get property values from an object may be susceptible. In this case it is likely that v-autocomplete is 'walking' the object and recursing.

This issue can be avoided by one of: a) Disabling the mechanism which constructs related getters (followRelationshipsData: false); b) using map or similar to extract only the attributes you need from the object; or c) using a library which doesn't walk objects.

Bartheleway commented 3 months ago

Cristal clear :) I don't know exactly how v-autocomplete from Vuetify works. Honestly I tried to look into the source but didn't understand at which point they might walk through the object. I find it a bit disapointing that mixing pinia-json with Vuetify oblige me to use map or disable followRelationshipsData instead of being able to use a record list out of the box.

I did some tests today:

I also tested to use the record list out of the box and check if any network calls were made. None appears. This makes me think issue is maybe with using shallowRef on items of the list thus this would say issue is more with Vue itself than with Vuetify. What do you think?

mrichar1 commented 3 months ago

Ultimately JSONAPI can return recursive relationships, and by default pinia-jsonapi will convert these to references which can be followed. If any code tries to follow these, then it will lead to infinite recursion.

I wouldn't expect any extra network calls to be made here - all the data is returned from the server and normalised in the original request.

While it is an extra step to prevent this recursion, if you don't need the referenced relationships, or there are attributes and relationships you aren't using, then it is a good idea generally to disable/remove these - not just for this issue, but for overall performance.

Perhaps the best way to do this (which I forgot to mention previously) is to use 'sparse fieldsets' on the JSONAPI request. This means that the server won't even try to calculate and return the data you don't need, making the response quicker and smaller: https://jsonapi.org/format/#fetching-sparse-fieldsets

Bartheleway commented 3 months ago

Yes using sparse field as well as followRelationshipsData are both valid approch with what you previously explained. Sparse fieldsets allowing more control about what is returned than just disabling all relationships.

Unfortunately in my case my API doesn't support (yet) sparse fieldsets. I will use the map or followRelationshipsData for now and check how to implement fieldsets eventually. I can work with that and very thankfull for explaination & solution provided.

To pursue the discussion about "code trying to follow relationship", I really don't have any code written that would do that. And I believe this is a side effect of shallowRef used internally by Vuetify and Vue. Do you believe there is any chance it comes from here / be solved ?