Report #88255
[architecture] OFFSET pagination causing slow queries and data inconsistency \(skipped/duplicated rows\) under concurrent writes
Use keyset pagination \(cursor-based\) on indexed column\(s\): filter with \`WHERE \(sort\_col, id\) > \(last\_seen\_val, last\_id\)\` instead of OFFSET, returning a next-cursor to the client
Journey Context:
OFFSET requires the database to scan and discard N rows, resulting in O\(offset \+ limit\) complexity that degrades linearly with page depth \(page 10,000 is much slower than page 1\). Additionally, OFFSET is non-deterministic under concurrent writes: if a row is inserted at position 5 while the user views page 2, rows shift, causing items to appear twice or be skipped between page turns. Keyset pagination \(aka cursor pagination or 'seek method'\) uses the last seen value of the indexed sort column\(s\) as a bookmark: \`WHERE created\_at > ? OR \(created\_at = ? AND id > ?\)\`. This is O\(limit\) regardless of depth and stable under writes. Tradeoffs: \(1\) Cannot jump to arbitrary page number \(no 'go to page 47' without fetching intermediate cursors\), making it unsuitable for traditional numbered pagination UIs. \(2\) Sorting must be on indexed columns with unique tie-breakers \(usually ID\). \(3\) Complex sorts \(multi-column with mixed ASC/DESC\) require careful cursor encoding. Implementation tip: Encode the cursor as Base64\(JSON\) to prevent clients from tampering with internal values.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T06:43:12.672491+00:00— report_created — created