-
Notifications
You must be signed in to change notification settings - Fork 496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LazyBookshelf optimizations #3726
base: master
Are you sure you want to change the base?
Conversation
I'm not sure if this is the case for the library view of sorting by size, but I noticed that some endpoints are calculating the size of the item based on the size of every file instead of using the I opened an issue that discussed this a bit here (noticed that a lot of my items still have a NULL size): #3673 If sorting by size is using the |
It was intentional to show the blank cards to indicate that an item is there and it just hasn't been loaded yet. Like a skeleton component. The flickering placeholder image is a bug that didn't come up until later and I haven't figured out why that happens. In When After the audiobookshelf/client/components/cards/LazyBookCard.vue Lines 17 to 22 in 858d697
I don't see where in that code the placeholder cover image would be shown. I haven't found a way to reliably reproduce the placeholder image flickering. I think we should show something there while the items are loading. I like the skeleton placeholder components better than using a loading indicator. Some users have really slow server responses and could see a blank screen for several seconds. |
Acknowledged. Working on it. |
OK, let me address the placeholder flicker bug first. You were right that my PR didn't address the root cause of that bug. I'll argue, though, that my PR does eliminate this bug inadvertantly (explanation later below). I'm able to reproduce the bug almost every time I try, by scrolling the mouse wheel very quickly and then stopping it abruptly. This is more visible when some of the slower Here's an example, using the current head code. You can see the flicker at around 00:08. Screen.Recording.2024-12-18.091510.mp4I think I now understand why this bug is happening. It is a combination of two things:
It all revolves around this line in LazyBookCard: <img cy-id="coverImage" v-show="libraryItem" ref="cover" :src="bookCoverSrc" class="relative w-full h-full transition-opacity duration-300" :class="showCoverBg ? 'object-contain' : 'object-fill'" @load="imageLoaded" :style="{ opacity: imageReady ? 1 : 0 }" /> But let me use a use a more simplifed version to illustrate what I think is happening: <img v-show="libraryItem" :src="bookCoverSrc" /> So here's what I believe is happening, chronologically.
<img style="display: none;" src="/book_placeholder.jpg" />
Now, if we were to change I tested this, and indeed, the flicker disappeared! Screen.Recording.2024-12-18.220906.mp4Now going back to my PR, this is why I claimed it inadvertantly fixed the issue — since it doesn't mount empty cards, then at no point is the card's Let me know if you have any questions/comments about this point. I'll address the other points separately. |
That makes sense, thanks for digging into it. |
Working on it.
…On Wed, Dec 18, 2024, 22:40 advplyr ***@***.***> wrote:
That makes sense, thanks for digging into it.
Are you adding back in the skeleton card since now we know how to prevent
the flicker?
—
Reply to this email directly, view it on GitHub
<#3726 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFMDFVTJHZKVRGYLRZDLXQL2GHMU7AVCNFSM6AAAAABTWUV5YOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKNJSGIZDQNRZGU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
OK, I tried to resolve this without mounting empty entity cards — I think it's wasteful and expensive to load empty entities that will not end up being shown, and it also shows strange artefacts like (like Instead, I put card skeletons into the empty shelves. Those are much lighter than cards and can be pre-loaded (like the empty shelves). I also fixed the root cause of the placeholder flicker. PTAL, and let me know what you think. |
Brief summary
The follwoing changes were made to optimize rendering of LazyBookshelf, especially during scrolling.
rebuild
directly instead ofexecuteRebuild
insettingsUpdated
passive: true
option to scroll event listenerWhich issue is fixed?
There's no existing issue.
In-depth Description
1. Debouncing when scroll rate is high
When scrolling using the mouse wheel (and, to a lesser extent, by dragging the scrollbar), scrolling events tend to fire very rapidly (up to ~60 times per second), and sometimes for a long while (like when setting the wheel to spin freely on a Logi Mx Master 3 like mine). This creates many redundant
handleScroll
calls (which, in turn, redundantly fetches and mounts many entities which are outside the visible display).To counter this, I calculate the scrolling rate on each scroll event, and debounce the
handleScroll
call for 25 ms if the scroll rate is higher than 5 pixels/ms. This saves a lot of redundant fetching and rendering, and usually during the end of scrolling the rate goes below 5, so the debounding happens mostly in the middle of the scroll period, but not in the end.Note that a side effect of this is that the bookshelf will go blank during a very fast and long scroll. I think this is acceptable, since you don't gain anything from seeing fast flying entities (the scroll bar is still moving, though, giving you an indication that scrolling happens)
2. Wait for entities to be fetched before mounting them
Up until now,
handleScroll
calledloadPage
but didn't wait for it to finish fetching the entities, and mounted empty entities, which caused the book placeholder flicker you sometimes saw. Now we only mount after all the relevant entities have been fetched.3. Cleaning up only after scrolling
Up until now, every handleScroll call did a cleanup of non-visible entities, with this code:
I suspect this cleanup was slowing rendering because of the frequest dom changes. Plus, it only handled book entities, and not other types of entities (which have different ids). I added a debounce of 500 ms here, and fixed the bug.
4. Calling
rebuild
directly instead ofexecuteRebuild
insettingsUpdated
Doing this got rid of 250 ms delay before rebuild, which caused an annoying bookshelf flicker when changing the cover size.
5. Adding
passive: true
option to scroll event listenerThis is supposed to enable smoother scrolling and improve scrolling performance when the listener doesn't need to call
preventDefault
.6. Some additional refacroting
While doing all of this, I made a number of refactoring changes:
rebuild
, and got rid ofremountEntities
pagesLoaded
now containsfetchEntities
promises instead of booleansfetchEntities
is now always called only fromloadPage
Things yet to be fixed
While testing this, I found that the server has very different response times for page fetches for different
sortBy
values. When sorting byAdded at
orTitle
, server response time is usually below 100 ms for a page fetch request. However when sorting bySize
, for example, the response time tends to go up significantly, and when scrolling fast, the server is handling a number of requests simultaneously, and then it behaves really bad (all simultaneous requests take 5 or more seconds) - we've already seen that Sequelize/SQlite handles concurrent requests very poorly, and I suspect that this is the issue here as well.I suspect that the poor performance on certain sortBy values is due to lack of proper indices, but will need to investigate further.
How have you tested this?
sortBy
andfilterBy
values