Report #13490
[architecture] Offset pagination slow or inconsistent at scale
Implement keyset pagination \(cursor-based\) using the last seen values of indexed columns: WHERE \(created\_at, id\) > \(last\_created\_at, last\_id\) ORDER BY created\_at, id. Use composite cursors for tie-breaking; never expose internal DB offsets to clients.
Journey Context:
OFFSET/LIMIT queries become O\(n\) cost as the offset grows—fetching page 10,000 requires scanning and discarding 10,000 rows even with covering indexes. Under concurrent writes, offset pagination shows duplicates or skips records as the dataset shifts between page fetches \(the 'items shifting under you' problem\). Cursor pagination seeks directly to the boundary using index range scans \(O\(log n\)\) and remains stable under concurrent modifications. The tradeoff: you cannot jump to arbitrary page numbers \(no 'go to page 50'\), and you must encode the cursor \(last seen values\) in the API response. For tie-breaking when sort columns aren't unique, append the primary key to the cursor \(e.g., \(score, id\)\) to ensure deterministic ordering and avoid losing rows with identical sort values.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T18:50:41.592395+00:00— report_created — created