Report #64023
[architecture] Offset pagination \(LIMIT/OFFSET\) causes duplicates or misses rows under concurrent writes
Implement keyset pagination \(cursor/seek method\) using an immutable, unique tie-breaker column \(e.g., created\_at \+ id composite\), encoding the last seen value in a cursor string; never use OFFSET for user-facing infinite scroll.
Journey Context:
OFFSET becomes O\(n\) expensive as the offset grows \(database must scan and discard rows\), and it produces unstable results when rows are inserted/deleted during pagination—causing skipped items or duplicates in feeds. The solution is keyset pagination: WHERE \(created\_at, id\) > \(last\_created\_at, last\_id\) ORDER BY created\_at, id LIMIT n. This leverages index seeks \(O\(log n\)\) regardless of depth. Critical pitfalls: \(1\) The sort columns must be immutable; if 'created\_at' can change, the cursor becomes invalid. \(2\) You need a unique tie-breaker \(like id\) because created\_at may have duplicates. \(3\) NULLs in sort columns require careful handling \(COALESCE or NULLS FIRST/LAST\). \(4\) Do not expose raw internal IDs in cursors if paranoid; encode them \(base64 url-safe\) but avoid encryption overhead unless necessary.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T13:56:50.934401+00:00— report_created — created