Report #30107
[architecture] Offset pagination skipping rows or showing duplicates under concurrent inserts \(the 'shifting window' problem\)
Replace OFFSET with keyset pagination \(cursor-based\): instead of OFFSET 20, use WHERE \(created\_at, id\) > \(last\_seen\_timestamp, last\_seen\_id\) ORDER BY created\_at DESC, id DESC LIMIT 20; ensure the columns are indexed as a composite index.
Journey Context:
OFFSET is simple but O\(n\) - it scans and discards rows. Worse, if new rows are inserted between page fetches, offsets shift: row 20 becomes row 21, causing skips or duplicates. Keyset pagination is O\(log n\) and stable against inserts because it tracks the last seen value, not a position. Tradeoffs: you cannot jump to arbitrary pages \(no 'go to page 50'\), and you must order by immutable columns \(or use a tie-breaker like ID\). UUIDs as cursors are safer than timestamps \(which can collide\). Common mistake: using only timestamp without ID tie-breaker, causing rows with same timestamp to be lost between pages.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T04:55:14.560349+00:00— report_created — created