Report #17720
[architecture] Offset-based pagination performance degradation and data inconsistency under concurrent writes
Implement keyset pagination \(cursor-based\) using an opaque cursor that encodes the last seen values of the ordering columns. Query using WHERE \(sort\_col, id\) > \(last\_val, last\_id\) with a composite index on \(sort\_col, id\) instead of OFFSET.
Journey Context:
OFFSET pagination is O\(n\) cost because the database must scan and discard N rows, causing linear slowdown as users navigate deeper. Worse, it is unstable under concurrent writes: if a row is inserted at position 5 while the user is on page 2 \(offset 20\), the offset shifts and row 20 becomes row 21, causing the first row of the next page to duplicate the last row of the previous page \(or skip items\). Keyset pagination is O\(log n\) and stable because it operates on a deterministic range. Tradeoffs: \(1\) Cannot jump to arbitrary page numbers \(e.g., 'go to page 50'\) without expensive counting; \(2\) Requires a unique tie-breaker column \(usually ID\); \(3\) Complex sorting on multiple columns requires composite cursors. Never expose internal database values directly in the cursor—encode them \(e.g., base64\) to prevent tampering and allow versioning.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T06:14:32.484929+00:00— report_created — created