Skip to content

[BUG] /books and /shelf page time out repeatedly and might be cause of high memory usage #290

Open

Description

@edsilberberg

2 pages that repeatedly stall on me. Taking discussion from https://discord.com/channels/1376676460647022752/1554508748276375582 and open GH issue as requested.

/authors responds in a timely manner, even /unmapped Comes back reasonably quickly.
/books and /shelf however do not populate or respond even if I am patient or walk away.
/ (main library tab) does respond quickly once Chaptarr gets past initial loading delay. Similar delay to readarr back in the day on first return of home page on browse. I Just did a human count on browsing from a closed session and got a count of 31 (Which is awfully close to potential 32nd time out on something, or a total coincidence) Home page load times probably deserves it's own report but adding as a portion of this specific /book and /shelf report as context. Can split if requested.
I found this thread when I was looking for high memory utilization as well. I normally run at a couple gig of memory But I've had it drain my server before and I have not yet added the memory cap parameter to this docker. It shot up to 20gig earlier today.
/authors responds in ~25 seconds. Test method is going directly to that url rather than / and then clicking authors.

direct jump to http://ip/books starts returning and goes to /books but is at pulsing circle. Has matching log entry of [Warn] BookController: [API-PERFORMANCE] Taking general book path without an author/book filter.

Same result twice, pulsing circle, the ~29 seconds by human count for /books to return but I never get data there, same log warning.
sqlite not postgres, unraid bypassing fuse for appdata and book library
with /shelf I gave up at a 2 minute count. memory usage definitely jumps when I try those particular pages.

Version
0.9.965.0
.NET Core
Yes (10.0.12)
Docker
Yes
Database
Sqlite 3.50.4
Database Migration
107
AppData Directory
/config
Startup Directory
/app
Mode

chaptarr.trace.txt
chaptarr.debug.txt

Image

Cons

Took discord suggestion from Golden_Dragons and switched from sqlite to postgress, same issue after pgloader import
Dropped chaptarr dbs and recreated and re-imported. Same results

also

chaptarr.debug (1).txt
message (1).txt
message.txt

spotted thread pool starvation each time
[Warn] Microsoft.AspNetCore.Server.Kestrel: As of "10/04/2026 17:20:56 +00:00", the heartbeat has been running for "00:00:06.0139203" which is longer than "00:00:01". This could be caused by thread pool starvation.

Activity

  1. bhoffman20 commented on Oct 4, 2026

    @bhoffman20
    Collaborator

    Checked against develop (e67374f). Your logs show the cause. /shelf, and /books in Table or Overview view, both call GET /api/v1/book?mediaType=… with no author or book filter. That request loads the whole library into memory before it filters anything:

    • GetBooksForDisplay calls GetAllBooks() (L2234). That reads all 643,135 book rows, for both media types, and only then filters to the requested type in memory.
    • It then calls GetAllMonitoredEditions() (L2267). That runs SELECT * FROM Editions WHERE Monitored = true across the whole library, wide JSON columns included (Chapters, Images, Links), before narrowing to the books it needs. Your OutOfMemoryException is thrown there.

    This fails the same way on SQLite and Postgres, so switching databases and re-importing wouldn't have changed it. The thread-pool-starvation warnings come from the same memory pressure.

    Related to #170 (item 3): same endpoint, but on a library about 8× smaller it was slow rather than out of memory. The /api/v1/author times in your log (50 s, then 143 s) fit #170 item 1.

  2. edsilberberg commented on Oct 4, 2026

    @edsilberberg
    Author

    Updated environmental info:
    Version
    0.9.965.0
    .NET Core
    Yes (10.0.12)
    Docker
    Yes
    Database
    Postgresql 18.6
    Database Migration
    107
    AppData Directory
    /config
    Startup Directory
    /app
    Mode
    Console
    Uptime
    01:29:05

  3. edsilberberg commented on Oct 4, 2026

    @edsilberberg
    Author

    db log entries taht may support report 170
    EDT [25406] ERROR: canceling statement due to user request
    EDT [25406] STATEMENT: SELECT "Editions".*
    FROM "Editions" WHERE ("Editions"."Monitored" = $1)

    EDT [25406] ERROR: canceling statement due to user request
    EDT [25406] STATEMENT: SELECT "Editions".*
    FROM "Editions" WHERE ("Editions"."Monitored" = $1)

  4. bhoffman20 commented on Oct 4, 2026

    @bhoffman20
    Collaborator

    Two possible fixes, from smallest to largest:

    1. Make the general /api/v1/book path load only what it needs (backend only). This should stop the crash.
    - Filter by media type in SQL instead of calling GetAllBooks() and filtering in memory.
    - Replace GetAllMonitoredEditions() with a query for the monitored editions of the books being returned. Leave out Chapters, which list mapping never reads and which is the largest column in Editions (129 of 219 MB in the #170 table). Read Overview only when include=overview is passed. FindByBook already chunks the IDs, but it also joins Book and Author on every edition, so this path needs its own leaner query.
    - Load series links once: GetAllBooks() loads them, and MapToResource loads them again.

    This roughly halves the rows read and drops the largest column. It still returns about 320k books for one media type in a single JSON response, which the server buffers in full and the browser holds in Redux. So it stops the crash but leaves the page slow.

    2. Page /shelf and the Table/Overview views on the server (frontend and API). This fixes the slowness too.

    • Table and Overview views can switch to the /book/paged and /book/ids endpoints that Posters view already uses for infinite scroll. Custom filters would need server-side support as well, or they'll keep loading the full list.
    • /shelf shows one row per author, and it fetches every book only to work out which authors to list and what goes in each row. It could build the rows from the author list and load each author's books as the row scrolls into view. The existing ?authorId= path is the heavy one, so that load needs a lean variant.

    Fix 1 is small and safe to ship first. Fix 2 is the real fix for libraries this size.

  5. bhoffman20 commented on Oct 4, 2026

    @bhoffman20
    Collaborator

    Reproduced locally on a synthetic SQLite library the same size as yours (643,599 books, half audiobook and half eBook), with the .NET heap capped at 4 GB:

    Build Request Result Time Peak memory Response
    develop /api/v1/book?mediaType=audiobook&include=author (Books page) 500, OutOfMemoryException in GetAllMonitoredEditions(), same as your logs 54 s 4.3 GB —
    Fix 1 (local) same 200 100 s 4.0 GB 1.6 GB
    Fix 1 (local) /api/v1/book?mediaType=audiobook (Shelf page) 200 46–129 s 3.5 GB 583 MB

    With fix 1 the server survives, but only just. The browser then runs out of memory parsing the 583 MB response on Shelf. So fix 1 on its own moves the failure from the server to the browser, and fix 2 (server-side paging) is needed for these pages to work at this size.

    Correction to my earlier comment: the server doesn't buffer the response. BufferingMiddleware buffers request bodies only. The memory goes into loading the rows and building the resources.

  6. bhoffman20 commented on Oct 4, 2026

    @bhoffman20
    Collaborator

    Workaround for /books until there's a fix, tested on the same 643k-book repro: switch to Posters view and pick one of the built-in filters (All, Monitored, Unmonitored, Exists, Missing, Wanted). That combination loads books a page at a time through /api/v1/book/paged instead of the full list, and the page loads fine.

    Table and Overview views, and custom filters, still load everything. /shelf has no equivalent yet.

  7. bhoffman20 commented on Oct 5, 2026

    @bhoffman20
    Collaborator

    Draft PRs for this, tested together on the 643k-book repro:

    With all three, /books (every view, built-in filters) and /shelf load normally and the server stayed under 1.3 GB. Custom filters on /books still load the full list.

  8. edsilberberg commented on Oct 7, 2026

    @edsilberberg
    Author

    25k files and 96k titles much better after filter, app stayed stable, new scrolling interim method is stable, but end user scroll pause is a smidge invasive feeling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions