Report #67829
[architecture] Slow pagination queries on large PostgreSQL tables using LIMIT and OFFSET
Replace OFFSET-based pagination with keyset pagination \(cursor-based\) using a composite index on \`\(created\_at, id\)\`. Query using \`WHERE \(created\_at, id\) > \(last\_timestamp, last\_id\) ORDER BY created\_at, id LIMIT 20\`. This maintains O\(log n\) performance regardless of page depth.
Journey Context:
OFFSET forces the database to scan and discard N rows before returning results; at page 10,000 with 20 items per page, it scans 200,000 rows linearly. Keyset navigation jumps directly to the boundary using the index. The critical implementation detail is the tie-breaker: timestamps aren't unique, so you must include a unique column \(like \`id\`\) in the composite index and WHERE clause to avoid missing rows with identical timestamps. The tradeoff is losing the ability to jump to arbitrary page numbers \(no 'go to page 50'\); you can only navigate sequentially. Don't use this if you need random access pagination or if your sort columns are highly volatile.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T20:19:55.964914+00:00— report_created — created