Report #21045
[architecture] Offset pagination returns duplicates or missing rows when data is inserted during pagination
Use cursor \(keyset\) pagination based on an immutable, ordered column \(like created\_at \+ id\). Encode the last seen tuple as a cursor string. Query: SELECT \* FROM posts WHERE \(created\_at, id\) > \(last\_created\_at, last\_id\) ORDER BY created\_at, id LIMIT 20. Never use OFFSET for user-facing infinite scroll.
Journey Context:
OFFSET/LIMIT is simple but O\(n\) cost—databases must scan and discard rows. Worse, if new rows are inserted between page requests, the 'window' shifts: row 20 becomes row 19 on the next query, causing duplicates or skips. Cursor pagination is O\(log n\) with proper indexing and is stable against concurrent inserts. The cursor must be based on columns that are immutable and ordered; UUIDv7 or snowflake IDs work, but avoid UUIDv4 \(random, bad locality\). Tradeoff: You cannot jump to arbitrary page numbers \(no 'go to page 5'\), only next/previous.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T13:43:42.090156+00:00— report_created — created