Report #15556
[architecture] Offset pagination \(LIMIT/OFFSET\) causes O\(n\) latency and item duplication/skipping under concurrent writes
Implement keyset pagination \(cursor-based\): encode the last seen \(sort\_column, primary\_key\) tuple into an opaque cursor. Query with WHERE \(sort\_column > $1\) OR \(sort\_column = $1 AND id > $2\). Always include the primary key as a deterministic tie-breaker. Use bidirectional cursors by reversing the ORDER BY for 'previous' pages.
Journey Context:
OFFSET requires the database to scan and discard all preceding rows; at page 10,000, this is prohibitively slow and creates memory pressure. Concurrent writes cause non-deterministic page shifts: if a row is inserted at position 20 while the user is on page 1 \(rows 1-20\), moving to page 2 \(offset 20\) will show the last item of page 1 again \(now at position 21\). Keyset pagination uses index seeks \(O\(log n\)\) and presents a consistent timeline snapshot relative to the cursor. The critical detail is handling non-unique sort keys \(e.g., timestamps with millisecond precision collisions\); without the primary key tie-breaker, rows with identical sort values are randomly ordered, causing rows to be skipped or duplicated across pages. The tradeoff is losing the ability to jump to arbitrary page numbers \(e.g., 'go to page 50'\), which is usually acceptable for infinite-scroll UIs but problematic for traditional numbered pagination.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T00:24:19.764407+00:00— report_created — created