Category: New Features & Improvements
Difficulty: High
Description:
The /api/events endpoint (indexer, line 235) and /api/settlements endpoints use offset-based pagination (limit + offset). Offset pagination has known problems for large datasets:
- Skipped/duplicate results when rows are inserted or deleted between pages
- Performance degradation:
OFFSET 10000 LIMIT 50 still scans 10,050 rows
- Inconsistent total counts under write-heavy workloads (the
total may change between pages)
Cursor-based pagination using indexed columns (initiatedAt, ledger, indexedAt) should be added as an alternative, following common patterns (e.g., Relay GraphQL Cursor Connections Spec).
Location:
services/indexer/src/index.ts:235–260, services/settlement-engine/src/index.ts:320–341, services/api-gateway/src/index.ts:772–796
Acceptance Criteria:
Technical Notes:
- For cursor pagination with
initiatedAt, use WHERE initiatedAt < :cursorDate ORDER BY initiatedAt DESC LIMIT :first.
- Ensure the column used for cursor has a database index.
- Encode cursor as
base64(JSON.stringify({ id, date })).
Category: New Features & Improvements
Difficulty: High
Description:
The
/api/eventsendpoint (indexer, line 235) and/api/settlementsendpoints use offset-based pagination (limit+offset). Offset pagination has known problems for large datasets:OFFSET 10000 LIMIT 50still scans 10,050 rowstotalmay change between pages)Cursor-based pagination using indexed columns (
initiatedAt,ledger,indexedAt) should be added as an alternative, following common patterns (e.g., Relay GraphQL Cursor Connections Spec).Location:
services/indexer/src/index.ts:235–260,services/settlement-engine/src/index.ts:320–341,services/api-gateway/src/index.ts:772–796Acceptance Criteria:
PaginationQueryinshared/validation/schemas.ts:cursor(opaque string),after(ISO date),first(int, replaces limit)encodeCursor(row)/decodeCursor(cursor)pair using base64 encodingGET /api/eventsGET /api/settlementsGET /api/settlementspageInfo: { hasNextPage, endCursor }in paginated responsesTechnical Notes:
initiatedAt, useWHERE initiatedAt < :cursorDate ORDER BY initiatedAt DESC LIMIT :first.base64(JSON.stringify({ id, date })).