Report #6165
[architecture] Duplicate or missing records in paginated API results when using cursor pagination on non-unique columns like timestamps
Always append a unique tie-breaker \(typically the primary key ID\) to the cursor when paginating on non-unique columns. Encode a composite cursor \(created\_at, id\). The query must use a composite WHERE clause: \(created\_at > $1\) OR \(created\_at = $1 AND id > $2\). Ensure the database index covers \(created\_at, id\) in that order to avoid a filesort.
Journey Context:
Cursor pagination \(keyset pagination\) is preferred over OFFSET for performance, but naive implementations use a timestamp like created\_at as the cursor. Since timestamps aren't unique \(multiple records created in same millisecond\), the query \(WHERE created\_at > last\_seen\) skips records with the same timestamp that weren't on the previous page, or returns duplicates if using >=. Developers often revert to OFFSET for 'tie-breaking,' reintroducing O\(n\) cost and instability under concurrent writes. The composite cursor solves this with minimal overhead but requires careful URL-safe encoding \(base64 of JSON or similar\) and handling of sort direction \(ASC/DESC\).
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T23:17:13.763387+00:00— report_created — created