Skip to content
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

Speed up playlist & collection query #3015

Closed

Conversation

BrianCArnold
Copy link
Contributor

Should fix #2852

…ists (otherwise, return the entire playlist)
…lections (otherwise, return the entire collection)
@BrianCArnold
Copy link
Contributor Author

The reason this works is that when we load the Collections and Playlists section, it requests all of the Collections/Playlists, as well as ALL of the metadata for every book in every Collection/Playlist.

If one has 100 playlists with 100 books each, that means we're loading the metadata for 10,000 books just to display the list of playlists, and then when a playlist is opened, we load those 100 books in that playlist again.

Instead, we only load 4 books for playlist, which should be sufficient to render the cover for each playlist, as the playlist consists of the covers of the first 4 books (if I understand correctly)

As for Collections, it appears that it loads the covers of the first 2 books, so the request for the list of collections only returns 2 books per collection.

Together, this vastly decreases the loading time when the total number of books in all collections/playlists is very large, in the hundreds to thousands of books, from multiple seconds to under 50ms

@@ -492,7 +492,7 @@ class LibraryController {
}

// TODO: Create paginated queries
let collections = await Database.collectionModel.getOldCollectionsJsonExpanded(req.user, req.library.id, include)
let collections = await Database.collectionModel.getOldCollectionsJsonExpanded(req.user, req.library.id, include, 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making this 2 at the API level, it would probably be better to make this an optional request parameter, with a default of "get everything" so 3rd party clients and scripts can still get all items from the single API call.

Not sure what a good name would be, maybe itemCount?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this one, I meant something like adding
itemCount: req.query.itemCount && !isNaN(req.query.itemCount) ? Number(req.query.itemCount) : undefined,
to the payload a few lines before, and then passing payload.itemCount to getOldCollectionsJsonExpanded instead of the constant value of 2.

@@ -513,7 +513,7 @@ class LibraryController {
*/
async getUserPlaylistsForLibrary(req, res) {
let playlistsForUser = await Database.playlistModel.getPlaylistsForUserAndLibrary(req.user.id, req.library.id)
playlistsForUser = await Promise.all(playlistsForUser.map(async (p) => p.getOldJsonExpanded()))
playlistsForUser = await Promise.all(playlistsForUser.map(async (p) => p.getLimitedOldJsonExpanded()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing as the collections endpoint, but looks like this function is written backwards for doing the paging at the database request level, so that may need to be swapped for the paging to actually work.

Copy link
Contributor

@nichwall nichwall May 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, it was already requesting everything from the database and then limiting the response in memory, so maybe the getUserPlaylistsForLibrary function needs to be restructured.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @nichwall you are right. I made a mistake initially it looks like and we are loading all the items twice.
I'm going to start fresh on this one instead of working off this PR to clean more of this up

Copy link
Owner

@advplyr advplyr May 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This explains the slow loading actually because we are iterating through every playlist and loading the playlistMediaItems when we should be getting them all in one go.
It would be helpful to also add an optional query param to limit the items returned in each playlist so I'll look into that also

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just updated this 1576164
I wasn't able to limit the media items in that query because of a sequelize bug sequelize/sequelize#12971
The only other way I can think to implement that is to filter out media items with an order < 5 or something like that.
It would be better to migrate that endpoint to return the new data model then to spend a lot more time trying to get the old one to perform better.

@advplyr
Copy link
Owner

advplyr commented May 26, 2024

The data model went through a migration from JSON objects in files to a relational db.

There are a bunch of places where more data is loaded than necessary because the new relational data model is mapping all the data onto the old JSON objects so that the entire API didn't have to get rewritten for the migration.

@BrianCArnold
Copy link
Contributor Author

I investigated the way that the query works, there's not really a way to limit the number of results in the included objects that I could see?

@advplyr
Copy link
Owner

advplyr commented May 28, 2024

See #3015 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: Slow loading playlists
3 participants