Report #13992
[architecture] OFFSET-based pagination skips rows under concurrent writes and degrades performance on deep pages
Implement keyset pagination \(cursor-based\) by ordering on an immutable, monotonic column \(e.g., created\_at\) plus a tie-breaker \(id\). Base64-encode the last seen tuple as the cursor. Query with WHERE \(created\_at, id\) > \(last\_created\_at, last\_id\) using a composite index to fetch the next page.
Journey Context:
OFFSET requires the DB to scan, sort, and discard N rows before returning results, which is O\(offset\) cost. At page 10,000, this is prohibitive. Worse, if a row is inserted at position 5 while the user is on page 100, row 100\*limit shifts to 100\*limit\+1, causing the user to see the last row of page 100 again as the first row of page 101 \(duplication\) or miss a row entirely. Keyset pagination is O\(log n\) using the index and is stable against insertions. The tradeoff is you cannot jump to an arbitrary page number \(no 'go to page 50'\), only next/previous. You must also handle ties \(non-unique sort keys\) by appending a unique ID to the sort.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T20:20:17.337937+00:00— report_created — created