Visitar URL original
perf(realtime): read hooks answer the same object until what they show moved; where-readers hear only their keys; useRecords throttleMs; a per-type store index by sebyx07 · Pull Request #930 · developerz-ai/ultimate · GitHub
Skip to content

perf(realtime): read hooks answer the same object until what they show moved; where-readers hear only their keys; useRecords throttleMs; a per-type store index - #930

Merged
sebyx07 merged 4 commits into
mainfrom
feat/realtime-store-coalesce
Oct 30, 2026
Merged

sebyx07 merged 4 commits into
mainfrom
feat/realtime-store-coalesce

Conversation

@sebyx07

@sebyx07 sebyx07 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What changed

The page store's read hooks no longer make a consumer undo their work by hand. Measured against tesote/bank-integrations' panel on 29.1.0. That app had built three workarounds: throttle.ts, because a live board redrew on every record (ten a second, on a phone), and stableRuns / sameData, because new arrays of the same rows defeated Solid's ===.

  1. The same answer back (record-answer.ts, keptAnswer). useQuery (live and non-live), useRecord and both useRecords forms now return the previous answer object while the status and every row object match. Across a ready ⇄ refreshing flip they keep at least the previous data array.
  2. A where reader is told only of keys it shows or that where keeps now (moves in use-record.ts). An answer nobody has read since the last notification is always told. A where that throws inside the listener counts as a move: the read raises it, and a throwing listener would otherwise silence the store's other listeners.
  3. useRecords(type, { throttleMs, schedule? }): opt-in and backward compatible. The first change is told at once, a burst becomes one notification at the end of the period, and the last change always lands. release() drops anything still owed. It reads no clock (the scheduler's timer is the period) and is validated by finiteCount (X_INVARIANT) before the type is held.
  4. The synced layer indexes keys by type (#byType). keyed(type) and the replay's ofType used to scan every row on the page with startsWith.

Measurement: bun run scripts/bench/record-store-bench.ts

Scenario Notifications New data arrays Time
board: 300 runs, board + alerts readers, 200 frames of others' runs 400 → 200 (alerts 200 → 0) 400 → 200 ≈ same (the sort is real work)
run page: 4 event readers + alerts, 180 fleet frames + 20 own events 260 → 80 (alerts 180 → 0) 260 → 80 23.4 → 9.2 ms
board with throttleMs: 1000, 10 frames/s for 20 s 200 → 21 200 → 21 29.4 → 7.8 ms
run page, 5,000 fleet runs held, 4 readers × 200 own events 800 (all real) 800 184 → 138 ms
useQuery refetch answering the same 50 rows, × 50 100 (the status flips are real) 50 → 0 —

Decisions (made without the owner, recorded here)

  • The wire is left as it is. Records frames already carry only the rows that moved. Resume already starts from the last contiguous seq per epoch. A gap already triggers one catch-up read and holds frames meanwhile. The store already sends one notification per frame. Nothing measured asked for more, so there is no protocol bump.
  • Throttling belongs to the reader, not the store. Store notifications stay synchronous: an optimistic write is visible before push returns, and the settle rules depend on that.
  • No new primitive and no new export. throttleMs / schedule are fields on the existing exported RecordSelection. No new error code.
  • Not breaking. data is typed readonly R[]. Code that mutated it now mutates a shared array, and the CHANGELOG says so. This is a minor release.
  • Budget, stated (axiom 9): examples/dummy /feed 166kb → 166.5kb. Measured 170,149 B against main's 169,509 B the same way (x build --target static): +640 B, page boot +359 and island +281, all of it the hooks above.
  • The bench is a tested script: record-store-bench.test.ts asserts the counts in the table. It is a regression test for the claims and keeps scripts/ coverage at its pin (83.42% / 89.64% locally).
  • Part of packages/realtime/CLAUDE.md (one bullet) moved to docs/history/realtime.md to stay under its size ceiling.

Checklist

  • x verify is green
  • Tier boundaries respected: realtime imports only @ultimat3/core/page
  • Tests added (use-records-quiet.test.ts, record-answer.test.ts, record-synced.test.ts). 6 of 9 hook tests fail on main's hooks
  • Errors are instructions: the only new throw is core's finiteCount (X_INVARIANT)
  • --json: the bench takes --json
  • i18n / colours / money / time: none touched
  • No any, no default exports
  • Public API documented: packages/realtime/README.md, wiki/Realtime.md, CHANGELOG.md [Unreleased]

Error codes

none

Verification

$ bun run verify   (local, no test services: live suites skip)
✓ typecheck ✓ lint ✓ boundaries ✓ filesize ✓ package-shape ✓ unit ✓ contract ✓ live ✓ job ✓ e2e ✓ eval ✓ manifest ✓ roadmap
✗ errors — X_TEST_BARE_ERROR at use-records-quiet.test.ts:99 (a throwing `where` predicate), fixed: the error is built once as the test's input
$ bun run scripts/verify.ts --only errors,lint
✓ lint ✓ errors
(drift, contract-diff, budgets, seo, i18n, policy: skipped at the repo root, as on main)

🤖 Generated with Claude Code

…w moved; where-readers hear only their keys; useRecords throttleMs; a per-type store index

Measured on bank-integrations' panel (29.1.0), which hand-rolled throttle.ts, stableRuns and
sameData around these: run page notifications 260 → 80 (the alerts reader 180 → 0), 23.4 → 9.2 ms;
a board at 10 records/s with throttleMs: 1000 told 21 times in 20 s instead of 200; 50 identical
refetches 50 → 0 new data arrays. scripts/bench/record-store.bench.ts replays it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@developerz-ai

developerz-ai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Fix failing CI on #930

Queued — waiting for a free box.


🤖 developerz.ai, an automated agent, not a human — developerz-ai[bot]

sebyx07 and others added 2 commits October 29, 2026 20:48
…feed's budget states its +518 B

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@developerz-ai

developerz-ai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ Reviewed — nothing blocking

0 actionable comment(s) · grounded on your code

⏱ 1m 08s wall clock · gpt-6.1-sol via router · 2 model call(s) · 232 output token(s)

🤖 developerz.ai — automated review, running on your model and your box.

@developerz-ai developerz-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary — 17 file(s), 2 finding(s).

Critical 0 · Major 1 · Minor 1 · Nit 0

Completed [bot]: reviewed answer reuse, filtered notifications, throttling, type indexing, tests, and benchmarks. Found a colon-containing type regression and a throttle teardown race.

🤖 developerz.ai review — automated, running on your model and your box. What is this? · Reviewed by router/gpt-6.1-sol on box hv-dedicated-6 — 508.4k in / 3.13k out over 10 calls; cost unknown: no price on record for router/gpt-6.1-sol.

Comment thread packages/realtime/src/record-answer.ts Outdated
Comment thread packages/realtime/src/record-synced.ts Outdated
…ter named (`?query:<name>` holds a colon); a throttled hook released while told arms no timer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@developerz-ai developerz-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary — 7 file(s), 0 finding(s).

[bot] completed — Reviewed the increment fixing timer teardown during reader notification and preserving colon-containing record types in the synced index, with regression tests and updated measurements. No concrete defects found in the changed lines.

🤖 developerz.ai review — automated, running on your model and your box. What is this? · Reviewed by router/gpt-6.1-sol on box hv-dedicated-1 — 158k in / 232 out over 2 calls; cost unknown: no price on record for router/gpt-6.1-sol.

@sebyx07
sebyx07 merged commit 6264331 into main Oct 30, 2026
16 checks passed
@sebyx07
sebyx07 deleted the feat/realtime-store-coalesce branch October 30, 2026 02:08
sebyx07 added a commit that referenced this pull request Oct 30, 2026
…ased] sections kept

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sebyx07 sebyx07 mentioned this pull request Oct 30, 2026
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.

1 participant