Skip to content

[P3.22] Dashboard renders a 100-book page with no pagination UI; first paint exceeds 15s on a large library #493

Description

@frankbria

Surfaced while triaging #488.

What happens

GET /api/v1/books/ returns up to 100 books (limit: int = Query(100, ge=1, le=100)), and the dashboard renders every one as a card. There is no pagination control in the UI, so a user past 100 books simply cannot reach the rest.

Measured on staging, where the E2E account holds 2,702 books:

  • 100-book payload: ~195 KB (~1.9 KB per book)
  • 100 cards rendered in one list
  • First paint exceeded 15 s late in a suite run — an E2E spec waiting 15 s for its book to appear captured a snapshot with zero cards rendered

That spec has been moved to 20 s to match its siblings (#488), which treats the symptom. The underlying slowness is this issue.

Why it is worth fixing

  • The remaining 2,602 books are unreachable. skip exists on the endpoint but nothing in the UI drives it.
  • A ~200 KB payload plus 100 card renders is a poor first-paint budget for the app's primary landing screen.
  • Ordering only recently became correct (fix(books): show newest books first on the dashboard #491 added sort(updated_at, -1); before that the page showed the oldest 100 forever). With ordering fixed, the missing pagination is now the binding limit.

Notes for whoever picks this up

  • owner_updated_idx (owner_id, updated_at DESC) already exists and backs the current sort — explain() shows LIMIT → FETCH → IXSCAN. Keep any paging change on that index; adding an _id tiebreaker measured as a blocking in-memory SORT stage.
  • Cursor-based paging (updated_at + _id as a cursor) avoids the skip/limit drift problem on a list that changes while you page through it, but note the index caveat above.
  • The list endpoint already projects out per-chapter content, so the payload is metadata only — the size is card count, not fat rows.

Priority

P3.22 — degraded UX and an unreachable tail, not a correctness or security problem, and it only bites accounts with large libraries. The staging E2E account is the extreme case; real users are unlikely to be near 100 yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions