Report #30333
[architecture] Missing or duplicate rows when paginating rapidly-changing data with cursor-based \(keyset\) pagination
Use a database snapshot \(REPEATABLE READ isolation\) for the entire pagination sequence, or include a strictly monotonic tie-breaker \(like id\) in the cursor and snapshot the first query's timestamp for subsequent pages.
Journey Context:
Keyset pagination \(WHERE created\_at > $cursor\) is faster than OFFSET for deep pages, but standard tutorials miss the consistency problem: if a row is inserted with a created\_at between page 1 and page 2, the 'next' cursor will skip it \(torn read\). Conversely, if using DESC order, you see duplicates. Simple fixes fail: using 'updated\_at' is worse \(touches on update\). The robust solutions are: \(1\) Snapshot isolation for the user's session \(expensive, can hold back vacuum\), or \(2\) Keyset by immutable ID plus a separate timestamp filter, accepting that you might miss very recent items.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T05:18:03.824203+00:00— report_created — created