Optimize home/library loading and SQLite query performance

- Replace home preview N+1 queries with a single window-function batch query
- Cache library previews and series card lists (15s TTL, shared with media cache)
- Tune SQLite: larger page cache, mmap, wal_autocheckpoint, ANALYZE on migrate
- Defer history fetch on library page until last_played sort is selected
- Yield to browser between paginated library loads to keep UI responsive

Co-authored-by: truewhile <truewhile@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-09-02 01:39:28 +00:00
parent deca7735a7
commit ba621cfc4a
10 changed files with 286 additions and 17 deletions
+41 -10
View File
@@ -2,6 +2,7 @@ package service
import (
"context"
"time"
"github.com/ShukeBta/MMTL/internal/model"
"github.com/ShukeBta/MMTL/internal/repository"
@@ -14,6 +15,10 @@ type LibraryPreviewItem struct {
Cards []SeriesCard `json:"cards"`
}
type libraryPreviewCacheValue struct {
Items []LibraryPreviewItem `json:"items"`
}
// ListLibraries returns every library configured on the server.
func (s *MediaService) ListLibraries(ctx context.Context) ([]model.Library, error) {
return s.repo.Library.List(ctx)
@@ -29,6 +34,18 @@ func (s *MediaService) ListLibrariesWithPreview(ctx context.Context, libraries [
return out, nil
}
visibility = ExpandMediaVisibilityForMergedCloudLibraries(ctx, s.repo, visibility)
filter := repository.MediaQueryFilter{
IncludeNSFW: visibility.IncludeNSFW,
AllowedLibraryIDs: visibility.AllowedLibraryIDs,
HiddenLibraryIDs: visibility.HiddenLibraryIDs,
}
cacheKey := s.libraryPreviewCacheKey(libraries, cardLimit, filter)
var cached libraryPreviewCacheValue
if s.cache != nil && s.cache.GetJSON(ctx, cacheKey, &cached) {
return cached.Items, nil
}
libIDs := make([]string, 0, len(libraries))
for i, lib := range libraries {
out[i] = LibraryPreviewItem{
@@ -39,13 +56,6 @@ func (s *MediaService) ListLibrariesWithPreview(ctx context.Context, libraries [
libIDs = append(libIDs, lib.ID)
}
visibility = ExpandMediaVisibilityForMergedCloudLibraries(ctx, s.repo, visibility)
filter := repository.MediaQueryFilter{
IncludeNSFW: visibility.IncludeNSFW,
AllowedLibraryIDs: visibility.AllowedLibraryIDs,
HiddenLibraryIDs: visibility.HiddenLibraryIDs,
}
counts, err := s.repo.Media.CountByLibraries(ctx, libIDs, filter)
if err != nil {
return nil, err
@@ -64,15 +74,32 @@ func (s *MediaService) ListLibrariesWithPreview(ctx context.Context, libraries [
fetchCount = 200
}
recentByLibrary, err := s.repo.Media.ListRecentByLibraries(ctx, libIDs, fetchCount, filter)
if err != nil {
return nil, err
}
allPreviewItems := make([]model.Media, 0, len(libIDs)*fetchCount)
for i := range out {
if out[i].Total == 0 {
continue
}
items, _, err := s.repo.Media.ListByLibrariesFiltered(ctx, []string{out[i].ID}, 0, fetchCount, filter)
if err != nil {
items := recentByLibrary[out[i].ID]
if len(items) == 0 {
continue
}
allPreviewItems = append(allPreviewItems, items...)
}
s.attachLibraryMetadata(ctx, allPreviewItems)
for i := range out {
if out[i].Total == 0 {
continue
}
items := recentByLibrary[out[i].ID]
if len(items) == 0 {
continue
}
s.attachLibraryMetadata(ctx, items)
cards := groupMediaSeriesCards(items)
if len(cards) > cardLimit {
cards = cards[:cardLimit]
@@ -83,6 +110,10 @@ func (s *MediaService) ListLibrariesWithPreview(ctx context.Context, libraries [
out[i].Cards = cards
}
if s.cache != nil {
s.cache.SetJSON(ctx, cacheKey, libraryPreviewCacheValue{Items: out}, time.Duration(s.mediaCacheTTLSeconds())*time.Second)
}
return out, nil
}