Report #61065
[architecture] Cursor pagination skips or duplicates rows when sorting on non-unique columns
Always append a unique column \(e.g., primary key\) as the final tie-breaker in the ORDER BY clause and include it in the cursor predicate. Use tuple comparison syntax \(e.g., WHERE \(score, id\) < \(last\_score, last\_id\)\) to ensure deterministic page boundaries even when multiple rows share the same sort value.
Journey Context:
Keyset pagination \(cursor-based\) uses the last seen value to fetch the next page. If you sort by a non-unique column like 'score' or 'created\_at', and multiple rows share that value, the cursor 'score=50' is ambiguous. The database may resume from a different row with score=50 on the next page, causing visible skips or duplicates to the user. Standard tutorials teach cursor pagination with single-column sorting, which fails in production when timestamps collide or scores cluster. The 'seek method' \(tuple comparison\) solves this by making the sort order strictly total. It requires the unique tie-breaker to be indexed for performance. This pattern is often missed until data volume reveals the pagination bugs.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T08:58:58.621262+00:00— report_created — created