Skip to content

perf(frontend): add stable pagination and snapshot behavior to transfer history - #297

Open
woahwhattheheck wants to merge 23 commits into
RemitFlow:mainfrom
woahwhattheheck:goat/remitflow-278-transfer-pagination
Open

woahwhattheheck wants to merge 23 commits into
RemitFlow:mainfrom
woahwhattheheck:goat/remitflow-278-transfer-pagination

Conversation

@woahwhattheheck

@woahwhattheheck woahwhattheheck commented Sep 24, 2026 •

Copy link
Copy Markdown

Closes #278

Summary

Transfer history uses a filter-scoped snapshot so concurrent inserts cannot duplicate or omit rows while paging. This continuation fixes recovery and selection behavior, prepares sort keys once, and repairs existing CI fixtures.

Behavior and compatibility

  • Freeze filtered membership in descending creation-time order with locale-aware ID ties and stable equal-key order. Keep every matching row, with five rows per page; no result cap or cache is introduced.
  • Bind cursors to the snapshot generation, filter scope and prior row key. Expired, malformed or out-of-scope cursors recover to a fresh first page. Snapshots expire after five minutes; filter changes reset them, and empty snapshots rebuild when loading supplies rows.
  • Mint distinct in-memory generation IDs even when replacement snapshots share a clock tick, filter and row count. An old cursor cannot alias that replacement.
  • Refresh existing members' live fields without reshuffling membership, including when their fields cease matching the filter. Retain captured records for vanished rows; new inserts appear in the next snapshot.
  • Clear explicit selections and select-all when filters change or a persisted snapshot generation is replaced. Preserve selection during same-generation refresh. URL filters and pagination controls remain compatible.
  • Keep the maintained review/confirmation/result/receipt flow and canonical decimal assertions. Offline blocking, retry, exactly-once creation and money checks remain. A valid amount that cannot be priced now receives the pricing explanation.

Snapshot performance

The sorter prepares timestamp/string-ID keys once per populated row and returns the original row references. It preserves locale ties, invalid-date fallback and undefined/sparse placement.

Measured calls to the actual snapshot constructor include record copying: Node 22.16.0, two warmups and five alternating paired samples per workload, with complete output equality:

  • 5,000 shuffled: 20.349 → 3.071 ms (6.63x).
  • 100,000 shuffled: 620.098 → 80.338 ms (7.72x).
  • 100,000 oldest-first: 37.541 → 28.876 ms (1.30x).

Separate untimed counting measured 3,058,470 → 100,000 timestamp parses for the large shuffled case. The tradeoff is one temporary key record per populated row; peak memory was not measured. Storage parsing, network/provider work and React rendering are excluded.

Benchmark, raw samples, exact source pins and focused execution.

Validation and current status

Current head: 1ee074a. Subsequent lookup, across-page selection, and inactive-date-filter changes retain their source-specific evidence below.

  • Earlier full selection: source 157ff7b passed 477 tests across 49 files, with zero failed/pending/todo/skipped. Node 24.19.0 / Vitest 4.1.10; 56.57 s reported, 57.113 s process. Existing formatting and production build passed (1.55 s Vite). All 344 tracked files stayed unchanged after execution.
  • Later sorter validation: source 2ae9335 passed 13 focused tests, zero failed/skipped, in 1.53 s; changed JavaScript passed Prettier and the production build passed (917 ms Vite), on Node 22.23.3. Those tested sorter bytes are retained in the current head. The earlier 477-test aggregate was not rerun or relabeled as a current whole-suite result.
  • Dated critical audit: the 4 October 2026, 11:56 UTC receipt exited 0 with 0 critical / 20 high / 12 moderate / 2 low. It is reused for identical package/lock bytes; remaining findings are not claimed repaired.

Aggregate commands, receipt and raw evidence. Hosted CI and Lighthouse require approval; maintainer acceptance remains pending.

Page lookup continuations

At 3bb8747d, page lookups retain only visible snapshot IDs rather than allocating a full-history Map and temporary pairs for each page. The lookup preserves the last duplicate-ID winner, exact live references and captured-record fallback for missing rows, with no persistent cache. Direct production-module measurement on Node 22.16/Linux: 100,000 rows with the default five-row page measured 14.269 → 1.606 ms median (8.89x); lookup cardinality fell from 100,000 to 5. Complete outputs and references matched across bounded mutation, insertion, duplicate, expiry and scope observations. Source pins, all four workloads, raw samples and runnable measurement.

Subsequent source afa3b0ef retains the bounded page map and avoids unnecessary live-history traversal. Ordinary arrays are scanned backward so the first match preserves the last-duplicate-wins policy; scanning stops after every visible ID is resolved. Empty pages read no live records. Missing IDs retain their snapshot fallback after a full scan. Sets, generators and custom array iterators keep their forward-only behavior.

Three focused assertion groups passed against the complete production module, and all six benchmark workload outputs and record references matched the preceding source. On Node 22.16, the 100,000-row ascending-input newest page measured 2.793942 → 0.007302 ms, and an empty filtered snapshot 1.590464 → 0.002271 ms. Full-scan controls were essentially unchanged: descending first page 2.754172 → 2.726743 ms; ascending last page 2.798958 → 2.804947 ms. Nine alternating pairs, twenty calls per sample. Source pins, reproduction program, raw samples and limits.

These results depend on input position and measure only the helper. Prior benchmark ratios must not be multiplied; no browser, provider or whole-application speedup is claimed. No Vitest suite, build or workflow was rerun for either lookup continuation; earlier validation remains source-specific. Maintainer acceptance remains pending.

Across-page deselection

Across-page deselection now converts the frozen membership to explicit IDs before removing a row or page, so other-page selections survive. The maintained selection integration file passes all five cases with no skipped cases on Node 24.21.0 and unchanged locked dependencies at production source 6ed4086f; both new cases fail against the preceding component. Scoped formatting and source-identity checks pass. Execution and limits.

Inactive date filter

At production source 9f1debe8, the All time filter defers its default Date allocation until a nonzero date range needs it. Supplied clocks, inclusive cutoffs, invalid dates and active ranges keep their behavior. Previous selection and pagination changes remain.

A native Node 22.16.0/Linux measurement of Array.filter calling the complete production helper over 100,000 records used three warmups and nine alternating paired samples. The All time median measured 12.115259 → 4.508355 ms (62.79% less time), with identical membership, order and object references; separate untimed clock allocations fell from 100,000 to zero. The active 30-day control measured 62.660949 → 60.724011 ms with 100,000 allocations in both implementations; that small difference is not treated as a material active-filter gain. Rendering, API and storage work are excluded.

Native run 37206173166 passes all eight unchanged transfers-filter integration cases at pinned source 9f1debe, including no-filter display, date presets and URL restoration. The original lock is installed; scoped formatting and clean-worktree checks pass. No test file or dependency was added. Source pins, raw samples, reproduction and limits.

These separate source-bound results are not combined into a current-head whole-suite total or a cumulative performance multiplier. No full build, deployed throughput, acceptance or payout is asserted for these continuations.

woahwhattheheck and others added 23 commits September 24, 2026 15:54
Replace offset paging with a filter-scoped snapshot so concurrent inserts
cannot duplicate or omit rows mid-scroll. Cursors carry a stable
(createdAt, id) tie-breaker; filter changes mint a new snapshot; expired
cursors recover by rebuilding from the live list.

Closes RemitFlow#278
Keep snapshot membership stable while looking up current row fields in the complete live transfer list. A pending row that settles during refresh now shows Completed without disappearing from the snapshot.

Add resolver and application-refresh regressions. Snapshot, filter and selection checks: 23 passed. Production build and critical audit gate passed. Full suite has 459 passes and 12 pre-existing failures; those same 12 failure names reproduce on untouched d02dc97. The four modified files pass Prettier; 10 unrelated baseline files still fail the repository-wide formatting gate.
Use the resolved snapshot count for the empty-state branch and selection totals. Settling the last pending match no longer hides retained rows, and concurrent inserts do not inflate the selected count. Preserve the resolver, cursor behavior and prior live-field refresh repair.

Extend the existing App snapshot integration file with all-settled and concurrent-insert regressions, retaining the three prior cases. A dependency-free observation of the exact exported resolver and verbatim page expressions improves from 5/9 to 9/9, with four corrected results and five unchanged controls under Node 24.19.0.

This observation does not mount React. The five-case maintained file, full JSX parse, formatting, lint, full suite, build, audit, browser screenshots and Lighthouse remain unrun for this continuation. Preserve historical parent validation and its recorded baseline failures; no dependency scan or install.
Append an invocation-local generation to automatically minted snapshot IDs. Equal-time/equal-size replacements can no longer accept old cursors or be dropped by the existing page identity comparison. Existing snapshots, caller-supplied IDs, filters, ordering and TTL are unchanged.

Validation: maintained test/unit/transfer-snapshot.test.js passed 11/11 under Node v22.23.3, using the locked package install. No application build, full suite or browser interaction was run. Run: https://github.com/woahwhattheheck/RemitFlow-Frontend/actions/runs/37192074014
Bind selection to the frozen generation that the user selected. When TTL or cursor recovery persists a replacement snapshot, clear both explicit IDs and select-all. Otherwise a newly inserted transfer silently becomes selected and removed rows remain counted. Same-generation refreshes, live status updates, pagination, filter resets, and the existing cursor-generation fix remain unchanged.

Actual mounted App regression on exact parent b8c2226: both new cases failed. The first observed the newcomer's checkbox checked after expiry; the second observed 1 transfer selected after its row disappeared. The baseline used a name filter for only these two new cases (five existing cases were not selected).

After correction the complete maintained test/integration/transfers-snapshot-pagination.test.jsx passed 7/7, zero failed or skipped, Vitest 4.1.10 / Node 24.19.0 / React and jsdom. Duration 9.19s, tests 6.48s. Existing frozen membership, filter reset, settled-row refresh, retained selection, and concurrent-insert cases all ran alongside both regressions. This is the real mounted App and repository localStorage transfer service, not a live payment or wallet execution.

Reproduction from repository root with its normal writable cache:
node node_modules/vitest/vitest.mjs run test/integration/transfers-snapshot-pagination.test.jsx --maxWorkers=1 --fileParallelism=false

The cloud run reused installed dependencies only after package.json, package-lock.json, Vite config, and test/setup.js matched the retained runtime. It imported the original Vite config with only cacheDir redirected to task scratch because the root filesystem was full; TMPDIR was similarly redirected. No install, source mocks, fake timers, unrelated test edits, full-suite/build, Lighthouse or hosted-CI pass is claimed. Only Date.now is controlled in the two expiry cases. Both changed files were formatted with existing Prettier 3.9.6.

Executed blobs: Transfers.jsx 38249e1; maintained regression fbb7f2c. Original PR297, issue278, branch and contribution custody are retained.

Attribution: GPT-6 Astra Pro / Relay-17 GrantFox delivery / ChatGPT cloud harness15a9c3b91fc7.
Continue original PR297 after snapshot selection recovery aa146d6. Four maintained test fixtures reuse the already-executed RF296 corrections only after all original fixture blobs match; confirmation/result navigation, exact amounts, offline blocking, retry/no-duplicate assertions, and canonical decimal expectations are preserved. Derive the one valid-amount/null-price explanation from this branch's SendMoney source. Format only the nine files flagged by existing Prettier3.9.6. No dependency/config changes, test removals, skips, timeout changes, or snapshot changes.

One complete package selection on this exact source tree a4af225: 477 passed, 0 failed, 0 pending/todo across49 files. Node24.19.0 / Vitest4.1.10; 2026-10-04 11:54:56.918428Z to11:55:54.031767Z,57.113s process/56.57s Vitest. All344 tracked files verified before/after. Existing Prettier check passed. npm run build passed in2.309s, Vite5.4.21 reported1.55s/119modules; same344 files unchanged.

The unchanged package625d0f3895fcc7afbfcfa131713249f5cd65dcd6 and lock70d29c1865dac7ce76ba7d4e6ca5ee454be54aae reuse the2026-10-04 10:00:59Z critical-audit receipt: exit0,0critical,20high/12moderate/2low. This is dated evidence for identical dependencies, not a newly refreshed advisory scan. Shared installed dependencies were reused only after package/lock/Vite/setup matches; cloud TMPDIR/cacheDir were task-local because root was full. No live provider, hosted CI, Lighthouse performance, merge, reward or settlement claim.

Existing source/contribution custody preserved. Durable raw gate evidence is added by the immediate documentation child before one non-force original-branch update.
Record exact source157ff7bffe98c96ad48b39c844ff51a0a070679a/treea4af225:477 passed,0 failed,0 skipped across49 files; existing Prettier and production build passed. Add the reviewable report, receipt and60,612-byte raw evidence ZIP (SHA256838969807560e6854a77c084694bf730e13ecd792046d2ad9d82860a358ec450). All344 executed application/test blobs remain unchanged.

Reuse Relay-17 root's newer11:56:36Z critical-audit execution only after identical package/lock bytes were verified in both checkouts.0critical,20high/12moderate/2low; no duplicate request and no noncritical remediation claim. Original donor run receipt and raw audit JSON retained. This source-visible update supersedes the earlier10:00:59Z audit note while keeping its dated statement accurate.

This documentation child and its tested source parent are published together through one non-force original-branch update. No hosted CI/Lighthouse, live transfer, sponsor acceptance, award or settlement claim.

Attribution: GPT-6 Astra Pro / Relay-17 GrantFox delivery / ChatGPT cloud harness15a9c3b91fc7.
Remove the summary receipt's optional authorship field. Preserve all source, runtime, donor, result and archive data.
Preserve full snapshot membership, native sparse-entry placement, stable equal-key order and locale-aware ID ties while avoiding timestamp parsing inside the comparator. Keep the existing pagination and recovery paths unchanged.

Add two focused regressions for ordering compatibility and one parse per populated row.
Preserve the concurrent receipt cleanup and the exact source/test bytes exercised in run 37201593676. Add source-bound constructor measurements and reproduction instructions. No execution workflow or dependency changes.
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 snapshot behavior to transfer history

1 participant