Skip to content

Commit ec0a426

Browse files
Merge pull request #733 from martoniel/Add/cursor-pagination
Closes #616: Add cursor pagination on /api/audit
2 parents 68989dd + 7c1d5ec commit ec0a426

4 files changed

Lines changed: 340 additions & 1 deletion

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Migration: 0025_audit_logs_cursor_index
2+
-- Replaces the single-column audit_logs_created_at_idx with a composite
3+
-- (created_at DESC, id DESC) index so that keyset-cursor pagination on
4+
-- GET /api/admin/audit is stable under concurrent inserts.
5+
--
6+
-- When two rows share the same created_at timestamp (common under high write
7+
-- concurrency) the previous single-column index left the tie-breaking order
8+
-- non-deterministic, meaning a page boundary could skip or repeat rows.
9+
-- The composite index makes (created_at, id) the canonical sort key, matching
10+
-- the ORDER BY and cursor predicate already used in auditLogRepo.ts.
11+
12+
-- Drop the old single-column index (no longer needed).
13+
DROP INDEX IF EXISTS audit_logs_created_at_idx;
14+
15+
-- Create the composite covering index used by the keyset cursor predicate:
16+
-- WHERE (created_at < $cursor_ts)
17+
-- OR (created_at = $cursor_ts AND id < $cursor_id)
18+
-- ORDER BY created_at DESC, id DESC
19+
CREATE INDEX IF NOT EXISTS audit_logs_created_at_id_idx
20+
ON audit_logs (created_at DESC, id DESC);

package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/db/schema.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,15 @@ export const auditLogs = pgTable(
403403
auditLogsCorrelationIdx: index("audit_logs_correlation_idx").on(
404404
t.correlationId,
405405
),
406-
auditLogsCreatedAtIdx: index("audit_logs_created_at_idx").on(t.createdAt),
406+
// Composite index for stable cursor pagination: ORDER BY created_at DESC, id DESC.
407+
// The (created_at, id) compound key is unique and monotone, so a keyset cursor
408+
// over it is stable even when rows with the same timestamp are inserted
409+
// concurrently — the id tie-breaker ensures no row is skipped or duplicated
410+
// across page boundaries.
411+
auditLogsCreatedAtIdIdx: index("audit_logs_created_at_id_idx").on(
412+
t.createdAt,
413+
t.id,
414+
),
407415
}),
408416
);
409417

0 commit comments

Comments
 (0)