Repository navigation
perf(read-state): load peer journals without a strict writeback - #1448
Conversation
a peer broadcast refreshed the local journal with save(identity), a strict readwrite transaction that re-validated and re-put an unchanged record in every listening window. load it through a readonly get with the same journal validation instead. a missing record still saves a new journal, and ready, flush and every mutation keep strict readwrite transactions. Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (storage/performance/lifecycle), Mantis (correctness/concurrency) and Groot (unread behavior) reviews.
No actionable production defects found. Source assessment: 9/10 for scope, ownership and correctness. Reviewed head 0c521a8 against base/merge-base 9366357.
The readonly peer load retains journal validation and the owner’s serial queue. Actual mutations, initialization and publisher reloads retain strict readwrite transactions. Missing-record and update-only adapter fallbacks preserve the existing behavior. I traced corrupt-record errors, pending/failed sync reconciliation, session disposal before community purge, and the unchanged diagnostics retry controls. The post-await closed guard prevents an already-started read from applying after disposal; closing the database itself does not cancel an existing transaction.
Optional test improvement: in src/features/relay/read-state.test.ts:624-626, polling a.revision() does not explicitly prove that disposed owner b has drained its held load. Await a completion barrier on b’s queue before the negative assertions (and release held work in finally). This strengthens the regression evidence; I found no corresponding production defect.
Reviewed the public description, both attached charts and feature-commit metadata; no disclosure finding. The reported performance numbers and test results were not independently reproduced. This was source-only: no tests, builds, app launch, PR-code execution or CI assessment. Real IndexedDB/multi-window behavior, native lifecycle, rendered focus/accessibility, performance and human acceptance remain unverified. This COMMENT is not approval or merge authorization.
…r-emoji-size * origin/main: (26 commits) perf(profiles): notify once per batch of owner attestations (#1650) feat(pi): load Pi durable extensions and providers from Pi packages (#1610) Teach agents memory links and source metadata; frame memory as information (#1562) Make avatar choices visible in agent creation (#1631) fix(pi): take plugin wakes instead of ignoring them (#1612) perf(agents): parse activity records once per snapshot (#1449) perf(terminal): parse hidden terminal output in slices (#1459) perf(composer): reuse the document projection for editor reads (#1463) perf(read-state): load peer journals without a strict writeback (#1448) perf(channels): keep openLink stable across tab changes (#1525) fix: name the Cargo package Buzz so the dev build shows "Buzz" (#789) test(browser): re-type a dropped page name in the search palette (#1635) test(browser): fix two main CI timing failures (#1567) feat(agents): deliver Attention watches and timers to conversations (#1578) fix(inbox): stop listing tag-only composer state as drafts (#1531) Choose Builderlab agent models at creation and in Settings (#1533) feat(communities): sync known communities through Builderlab accounts (#644) Tag the last-tagged agent when a Solo chat composer is shown empty (#1615) perf(usage): read Claude usage once, then only what changed (#1586) feat(chats): ask to share access when sending a chat link into a DM (#1614) ...
Summary
On a
BroadcastChannelchange notice, each read-state owner refreshed withsave((current) => current). That is a strictreadwritetransaction: it does a get, validates the journal twice and puts the unchanged record back, in every listening window, for every notice. The peer's save was already durable, so the writeback did no useful work, and the readwrite lock made it contend with the writing window.Now the notice handler calls
load():load()reads through a new optionalReadStateStorage.read(). That is onereadonlyget with the samereadJournalvalidation.save, which creates the journal as before. This is the only case where refresh still writes. Every real change has already been written by the window that made it.readJournalnormalization is deterministic, andreadyand the next mutation persist it.ready, flush's locked reload and every mutation keep their strictreadwritetransactions. A corrupt record is still rejected without being replaced.readis optional: test fakes that implement onlyupdatekeep the previous refresh path.Evidence
Paired base/candidate runs on a dedicated macOS 26.6 host (Apple M4 Pro VM, 6 CPUs, load ≤4). The harness is a Playwright source fixture with real IndexedDB and BroadcastChannel in Chromium and WebKit:
maincommit. The candidate differs only in this commit; its production blobs are identical to this branch's.Transactions:
readwritetransactions with 7,632 puts. The candidate ran 7,632readonlytransactions with 0 puts.readwrite.Medians at reserve 5,000 (365 KB journal). The candidate was lower in 12 of 12 rounds unless noted:
Correctness controls:
Unit tests:
Revocation boundary (external diagnostic, 192/192 cases, base and candidate):
createCommunities: dispose the read owner, thenpurgeCommunityDeviceState.This covers the disposal/purge boundary, not the membership-removal service end to end.
Checks at this head (on the measurement host with Hermit active, plus hosted CI):
pnpm checkpasses.vitest run: 708 files and 10,953 tests pass../bin/node --test tests/integration/*.test.mjs: 224 pass, 2 fail, 3 skipped. Clean base gives the same result.plugin-cli,plugin-manager-lock) come from theircargo build --lockedstep.webpki-roots1.0.10.read-reserve,unread,sidebar-unread,thread-unread,inbox-reading,navigation-mute-read) pass 58/58 in Chromium and WebKit.Limits: