Skip to content

feat: cursor/snapshot pagination for stream history - #143

Open
euniceamoni wants to merge 1 commit into
StreamPay-Organization:mainfrom
euniceamoni:feat/stream-history-pagination
Open

euniceamoni wants to merge 1 commit into
StreamPay-Organization:mainfrom
euniceamoni:feat/stream-history-pagination

Conversation

@euniceamoni

Copy link
Copy Markdown

Problem

History pages produced duplicate rows and gaps when new stream events arrived between page fetches, and had no bounds on result size.

Solution

  • streamHistory.js: snapshot pagination engine

    • On first-page fetch the full filtered+sorted event list is frozen into a Map<snapshotId, events[]>. The cursor carries {snapshotId, offset, filters} so subsequent pages always read from the same frozen slice — concurrent inserts cannot shift page windows.
    • Stable sort: (timestamp DESC, id ASC) breaks ties deterministically.
    • Filters (status, token, direction) are baked into the snapshot; changing a filter creates a new snapshot, preventing stale pages.
    • Page size capped at MAX_PAGE_SIZE=50; default 20.
    • LRU eviction keeps at most 20 snapshots in memory.
  • api.js: withLatency accepts an optional AbortSignal

    • Rejects immediately with AbortError on pre-aborted signals.
    • Clears the setTimeout and rejects on mid-flight aborts.
  • streams.js: emits StreamEvents on create / withdraw / cancel

    • Seed data also emits events for existing streams.
  • useStreamHistory.js hook

    • Cursor stack enables prev-page navigation without re-snapshotting.
    • New AbortController per fetch; previous request aborted before next fires, eliminating pagination race conditions.
    • Filter changes reset the cursor stack and trigger a fresh snapshot.
  • StreamHistoryTable component + CSS

    • Filter dropdowns for status, token, direction.
    • Prev/Next pagination with disabled states.
    • ARIA live regions (polite on body, pagination info).
    • Styled with existing design tokens.

Tests (39 total, 23 new)

  • stableSort: ordering, tie-break, determinism on 200 events, no mutation
  • applyFilters: each filter dimension, conjunctive, empty passthrough
  • snapshot pagination: concurrent inserts blocked, no duplicates/gaps
  • filter contract: cursor carries filters, new filters = new snapshot
  • page size cap: >MAX clamped, 0→1 minimum, exact MAX allowed
  • cancellation: pre-aborted and mid-flight both reject with AbortError
  • large fixture: 200 events with timestamp collisions, filtered totals
  • regression: 12 events + 3 concurrent inserts, zero duplicates/gaps closes perf(frontend): add stable pagination and filtering for stream history #134

Problem
-------
History pages produced duplicate rows and gaps when new stream events
arrived between page fetches, and had no bounds on result size.

Solution
--------
- streamHistory.js: snapshot pagination engine
  * On first-page fetch the full filtered+sorted event list is frozen
    into a Map<snapshotId, events[]>.  The cursor carries
    {snapshotId, offset, filters} so subsequent pages always read from
    the same frozen slice — concurrent inserts cannot shift page windows.
  * Stable sort: (timestamp DESC, id ASC) breaks ties deterministically.
  * Filters (status, token, direction) are baked into the snapshot;
    changing a filter creates a new snapshot, preventing stale pages.
  * Page size capped at MAX_PAGE_SIZE=50; default 20.
  * LRU eviction keeps at most 20 snapshots in memory.

- api.js: withLatency accepts an optional AbortSignal
  * Rejects immediately with AbortError on pre-aborted signals.
  * Clears the setTimeout and rejects on mid-flight aborts.

- streams.js: emits StreamEvents on create / withdraw / cancel
  * Seed data also emits events for existing streams.

- useStreamHistory.js hook
  * Cursor stack enables prev-page navigation without re-snapshotting.
  * New AbortController per fetch; previous request aborted before next
    fires, eliminating pagination race conditions.
  * Filter changes reset the cursor stack and trigger a fresh snapshot.

- StreamHistoryTable component + CSS
  * Filter dropdowns for status, token, direction.
  * Prev/Next pagination with disabled states.
  * ARIA live regions (polite on body, pagination info).
  * Styled with existing design tokens.

Tests (39 total, 23 new)
------------------------
- stableSort: ordering, tie-break, determinism on 200 events, no mutation
- applyFilters: each filter dimension, conjunctive, empty passthrough
- snapshot pagination: concurrent inserts blocked, no duplicates/gaps
- filter contract: cursor carries filters, new filters = new snapshot
- page size cap: >MAX clamped, 0→1 minimum, exact MAX allowed
- cancellation: pre-aborted and mid-flight both reject with AbortError
- large fixture: 200 events with timestamp collisions, filtered totals
- regression: 12 events + 3 concurrent inserts, zero duplicates/gaps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(frontend): add stable pagination and filtering for stream history

1 participant