Repository navigation
feat: cursor/snapshot pagination for stream history - #143
Open
euniceamoni wants to merge 1 commit into
Open
euniceamoni wants to merge 1 commit into
euniceamoni wants to merge 1 commit into
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
api.js: withLatency accepts an optional AbortSignal
streams.js: emits StreamEvents on create / withdraw / cancel
useStreamHistory.js hook
StreamHistoryTable component + CSS
Tests (39 total, 23 new)