Report #30664
[architecture] Offset pagination is slow and returns duplicate/missed rows under concurrent writes
Implement cursor-based \(keyset\) pagination using an immutable indexed column \(e.g., \`created\_at\` with \`id\` as tie-breaker\). Encode the last seen values into an opaque cursor. Query using \`WHERE \(created\_at, id\) > \(last\_created\_at, last\_id\)\` with appropriate composite index. This gives O\(log n\) seek time and stable results under concurrent insertions.
Journey Context:
OFFSET/LIMIT pagination suffers from two critical flaws at scale: 1\) Performance degrades linearly \(O\(n\)\) because the database must scan and discard all preceding rows to reach the offset. Page 10,000 requires scanning 10,000 \* page\_size rows. 2\) Stability issues: if a row is inserted or deleted while the user is paginating, the window 'shifts,' causing rows to be skipped or duplicated across pages. Cursor pagination solves this by anchoring to actual data values \(the 'cursor'\) rather than a relative offset. It requires an immutable ordered column \(or composite\) and a comparison operator. The tradeoff is that you cannot jump to arbitrary page numbers \(no 'go to page 50'\), and implementation is slightly more complex \(encoding/decoding cursors\). However, for infinite scroll or stable data exports, it is the only scalable solution.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T05:51:15.220300+00:00— report_created — created