Skip to content

Commit bbd20fa

Browse files
tlongwell-blockDawnWrenPerciEva
authored
perf(desktop): move five hot renderer paths from JS into Rust (#6024)
## Summary Moves five hot renderer paths out of JavaScript and into Rust, targeting main-thread CPU on the desktop app: wakeups, IPC round trips, and per-frame JS work during reconnect storms, catch-up, and archive sync. Five commits, reviewed and ratified sequentially (each round included independent re-review, mutation-tested coverage, and full-suite gates at pinned heads): | Commit | Change | |---|---| | `9b5a82ab9` | Batch inbound relay frames in the native websocket plugin: N run-loop wakeups collapse into 1 per 8ms window, byte-bounded so no batch crosses the Tauri direct-execute threshold onto the slow fetch path. NIP-42 AUTH bypasses the window. | | `8b047a35e` | Move the local-archive subscription into Rust: the renderer no longer sees archived frames at all. Shared `native_relay_client` (one authenticated socket per relay+pubkey, declarative subscription reconciliation, CLOSED-driven retry with write-time eviction), lease/epoch lifecycle ownership so delayed IPC cannot resurrect a stopped task. | | `609962b49` | Move persona catalog fetching into Rust: signature verification (~473us/event, ~0.24s per 500-event page formerly on the webview thread) now runs under `spawn_blocking`; one projected DTO crosses IPC. | | `38afea0a0` | Batch unread catch-up behind one IPC call: N per-channel REQs/awaits become one command with global two-pass classification, making the completion-order race unrepresentable. Both IPC DTO surfaces pinned by wire tests asserting serde output against the TS contract. | | `b955f528b` | Move observed unread state into native SQLite: scoped WAL DB with a sequence/revision protocol, transactional localStorage migration, epoch-tagged rebuild detection. Membership no longer serializes five capped arrays per catch-up: measured 327.4 KiB before, 0.2 KiB after at the same populated 5x1000 fixture. | Full design rationale, failure matrices, and verification detail are in the individual commit messages. ### Known behavior notes - **Native migration is a one-way door per profile.** A successful native open sets `migration_complete` in the first open's transaction and the renderer then deletes the legacy localStorage key (`useObservedUnreadPersistence.ts:290-294`, `observed_unread.rs:420-442`). The one-release JS fallback remains for native open *failure*, but a profile that has completed migration cannot return to the JS store. Relevant if a rollback path is ever needed. - **The membership seed is built unconditionally at startup** (`useUnreadChannels.ts:245-250`), before any `isNative()` check. It is ref-cached and small, but it is a startup cost the baseline does not pay, including on the fallback path. ### Related issue None found. Work originated and was reviewed in the Buzz `buzz-gui-performance` channel. ### Testing **Current head is `9a8128ba4d2d695113505162be388fcec62cda0a`. For receipts at that head, see [Round-3 receipts](#round-3--final-head-9a8128ba4) at the bottom; the block immediately below is the historical record for the original five-commit pack and its counts are superseded.** The receipts in this subsection are at exact pack head `b955f528b6dfa34db089b21cc718fba0f9284d6f` (tree oid `dcc711a66`), verified by three independent instruments (implementer, reviewer, gatekeeper), with `git rev-parse HEAD` confirmed in the same shell as each run: - `cargo test --lib` (full package): **2511 passed / 0 failed** (+7 +3 in the other targets) - `pnpm test`: **4920 / 4920 across 74 suites** - `cargo fmt --check`, `clippy --all-targets -D warnings`, `tsc --noEmit`, `pnpm check` (including the file-size ratchet): all clean - Mutation controls on the final review round: badge-lane controls A and B each turn exactly one test red; seed-guard (M4) and anchor-monotonicity (M5) mutants each killed by exactly their own witness; restored bytes fully green - The observed-unread native path is exercised by an in-tree `__TAURI_INTERNALS__` protocol rig (entry, replay, gap, rebuild, markers, membership, badge lane), added after review found the original suite never left the localStorage fallback **Open gates, stated plainly (status as of head `9a8128ba4`):** - **Paired CPU benchmark still not run.** The three-arm design (baseline `78cbffeb6` vs pack vs a null arm with native forced off, fresh profile per arm) was instrument-reviewed but never executed. The IPC payload reduction above is measured; end-to-end CPU improvement is predicted, not measured. This gate is unchanged since `b955f528b`. - **Live-local gate at the final head: GREEN at `9a8128ba4`.** A real native Tauri live-local pass per `TESTING.md` was run at this exact head. Receipts: `.scratch/pr6024-final-9a8128ba4d2d695113505162be388fcec62cda0a/`, `MANIFEST.txt` `start_utc=2026-08-17T21:58:07Z`, `FINAL_ATTESTATION.txt` `end_utc=2026-08-17T22:20:47Z` — both after the commit's `21:32:51Z`, and the build worktree's HEAD is `9a8128ba4` with a clean tree. **I opened these files rather than relaying the summary**, and recomputed 6 SHA256SUMS entries against the sealed manifest (6/6 match). From the raw artifacts: historical catch-up persisted 10 seeded events into the native `observed-unread.db`; a realtime event advanced it 10→11; the relay was killed and restarted with Desktop up and an event sent after recovery advanced it 11→12 within 2 polling ticks; Desktop was then stopped, an event sent offline, and after relaunch with the appdir preserved catch-up advanced it 12→13. `relay.log` shows 2 app WebSocket connections, the restart-close signal, and fresh connections after each recovery — the app-attributable socket evidence the previous round lacked. The preserved appdir database is still on disk and `select count(*) from observed_events` returns **13**. The ignored real-relay archive wire test passed **1/1** at this head. This exercises the `lib.rs` / `e2eBridge.ts` merge recombination under a real app and blocker 2's failure-mode family (relay killed mid-session, app killed with offline traffic in flight, sync did not go dead). It does **not** directly force the precise overlapping B/C archive-ownership timing window through the GUI; that invariant is pinned by the compiled `drop(owner)` mutation proof below, and is deliberately not relabelled as live race evidence. **Retraction retained for the record:** an earlier revision of this description said live-local was GREEN at `9a8128ba4` while citing a run executed against `f079d0914`, the pre-fix head, roughly three hours before `9a8128ba4` existed. That claim was false and was corrected before this run existed. The GREEN above rests on the new receipts only. - **Workspace-wide `cargo test --workspace` is red on `main`, not on this PR.** The exact-head live run's workspace logs contain honest `rc=101` results. All are in crates this PR does not touch: `git diff origin/main...9a8128b` is **empty** for `crates/git-sign-nostr`, `crates/buzz-relay`, and `crates/buzz-pair-relay`. `git-sign-nostr::tests::test_parse_envelope_rejects_invalid_oa_pubkey` fails **deterministically on clean `main`** — reproduced independently in fresh worktrees at merge-base `a282e0643` and at current tip `7f61cf431` (55 passed / 1 failed, same assertion at `lib.rs:2136`). Root cause: `nostr` 0.36→0.44 changed `PublicKey::from_hex` from a curve-point parse to a plain hex decode, so an all-zeros (off-curve) key now parses; measured with a standalone probe, `from_hex("0"*64).is_ok()` is `false` on 0.36.0 and `true` on 0.44.7. Filed as #6175, with the observation that nothing in CI runs this crate's tests (`just test-unit` enumerates packages and omits it; `server-cross-compile` compiles it and runs nothing). The mesh-demo and pair-relay failures in the same logs are timing flakes in equally untouched crates. **None of these gate this PR.** - **Full smoke + integration: GREEN at `9a8128ba4`.** CI run 32072147445 is `success` with every job green — all 4 Desktop Smoke E2E shards, both Desktop E2E Integration shards, Desktop Core, Desktop Build (macOS), Desktop E2E Relay, Rust Lint, Windows Rust, DCO, Dead Token Reference Guard. Attempt 1 was cancelled by an infrastructure hang, not by this code: both integration shards stalled 19m37s inside `Install Playwright system dependencies` (an apt install that runs before the e2e bundle is built or the relay starts), so no test executed and all downstream steps were skipped. The same step took 16s at `9128b9389` and 12s/18s on attempt 2 of these same bytes. Attempt 2 was a job rerun, not a push; the head is unchanged. --- Opened by Eva (agent) on Tyler's behalf at his request. --- ## Review round 2 — head `9128b9389` Carl's review [4953970089](#6024 (review)) raised two blockers against `f079d0914`. Both were confirmed real at the source by a second reader and both are fixed in `9128b9389`. **Blocker 1 — recovery dropped the mutation whose ingest failed.** Every captured mutation now gets one retry after `reopen()` refreshes sequence/revision, which is safe because ingest is idempotent (events upsert `DO NOTHING`, channel latest advances by `MAX`, membership is `INSERT OR IGNORE`, and a replayed sequence returns a snapshot rather than reapplying). `snapshotRequired` takes the same recovery path instead of being treated as success. If reopen and retry both fail, native is explicitly degraded and equivalent state is applied to the JS fallback so `isNative()` stops reporting healthy. Regressions assert the *originally rejected* marker, destructive clear, and membership delta each survive; removing the retry takes 5 tests red including all three. **Blocker 2 — a superseded archive start could tear down the newer scope's relay session.** `begin` now returns an `ArchiveOwnership` token that holds both ownership guards, and `archive_session` requires one, so session acquisition happens inside the ownership critical section rather than after it. ### Deviation from the requested regression test for blocker 2 Carl asked for "a production-wiring regression that pauses B after `begin`, fully installs C, resumes B, and proves only C's session and subscriptions remain live." **We did not write that test, deliberately, and wrote a stronger one instead.** Under this fix that scenario is unreachable by construction. The fix is not a revalidation check that a test could observe passing or failing — it is a type-level fence: `begin` hands the winner a token that holds the ownership locks, and `archive_session` cannot be called without one. A test that staged "B pauses after `begin`, C fully installs" would be staging a state the type system now forbids, so it could only pass vacuously. Two things close the blocker, and only one of them is a test: 1. *A superseded start cannot reach `archive_session` at all.* Enforced by the compiler, not by a test. `ArchiveOwnership` is un-constructible outside `archive::sync`. Verified by attempting both bypasses: calling `archive_session` without a token fails **E0061**, and forging the token from a real non-archive caller fails **E0451** ("fields `_latest` and `_running` are private"), with a second independent barrier because `RunningSync` is itself a private type. 2. *While an owner holds the token, no newer start can claim.* This is the property the token's usefulness rests on, and it is what the new regression pins — `a_newer_start_cannot_claim_while_the_owner_holds_its_token`. That test is aimed at the one mutant that actually threatens this design: keeping the token but releasing the guards inside `begin`, which is what someone reaches for to avoid holding a lock across an await. It **compiles**, it keeps every other lifecycle test green, and it restores the exact race. Against it the new test goes **single-red** — the other 26 tests in the module stay green. Proving mutual exclusion over the whole claim → acquire → install unit is strictly stronger than staging a pause the type system forbids, which is why we chose it. Also recorded in the code, in `ArchiveOwnership`'s doc comment: what this token does **not** cover. It serializes archive lifecycle against archive lifecycle, not against a plain `NativeRelayClient::session()` call from a non-archive feature. That window predates this PR and is theoretical today given renderer gating, so it is documented rather than fixed here. ### Round-2 receipts at `9128b9389` - `cargo test --lib`: **2513 passed / 0 failed** - `pnpm test`: **4926 / 4926** - `cargo clippy --all-targets -D warnings`, `cargo fmt --check`, `pnpm check`: rc 0 - Merged against `origin/main` in a scratch worktree (clean, zero conflicts) and re-gated on that merged tree: **2544** Rust / **4956** frontend, both 0 failed --- ## Round 3 — final head `9a8128ba4` `9a8128ba4d2d695113505162be388fcec62cda0a` is a merge of `origin/main` (`a282e0643`) into the pack branch, taken **before** final verification so that verification would run on the bytes CI builds rather than on a head that was about to move. The merge is textually clean but touches two files the pack also touches, `desktop/src-tauri/src/lib.rs` and `desktop/src/testing/e2eBridge.ts`, so recombination was checked explicitly rather than inferred from the absence of conflicts: **all 342 pack-added lines in those files survive (0 missing), and all 17 main-added lines survive (0 missing)** — no silent revert in either direction. Main's contribution is a `#[doc(hidden)] pub use print_agent_access_owner_only_probe_if_requested` in `lib.rs`, plus a Bumble→Pollen rename and workflow revision / `workflowUpdateError` mocks in `e2eBridge.ts`. ### Receipts at `9a8128ba4` Implementer, in a shell with `git rev-parse HEAD` confirmed at this SHA: - `cargo test --lib` (full package): **2544 passed / 0 failed** / 18 ignored - `pnpm test`: **4956 / 4956** across 74 suites - `cargo clippy --all-targets -D warnings`, `cargo fmt --check`, `pnpm check`: rc 0 - The 4 remaining `pnpm check` findings (`channelMutesStorage.test.mjs`, `channelStarsStorage.test.mjs`, `terminal.css`, `empty-edit-delete.spec.ts`) are all in files this PR never touches - All 7 pre-push hooks green, including `branch-skew`, which now passes on its own because the branch contains `origin/main` Independent reviewer, same exact head, own instruments: - `cargo test --workspace`: green; `cargo clippy --workspace --all-targets -- -D warnings`: green *(a later workspace run at `9a8128ba4` hit pre-existing `main`-side failures in untouched crates — see the workspace-gate entry under Open gates above; the difference is flake timing plus which run happened to reach `git-sign-nostr`, not a change in this PR)* - `pnpm test`: **4956 / 4956**; `pnpm check`: rc 0, same known findings outside touched files - **Mutation proof of the blocker-2 fix:** dropping the ownership token's hold (`drop(owner)`) turns `a_newer_start_cannot_claim_while_the_owner_holds_its_token` red; restoring the shipped bytes returns the suite green. The invariant that a superseded start cannot claim or disturb a newer archive session is pinned by a test, not just by the type system. - **Live-local at this exact head: attested and verified.** The mis-cited `f079d0914` run that originally appeared here is retracted; a real run at `9a8128ba4` replaced it, and its receipts were opened file-by-file (not relayed) by a second agent. See the live-local entry under Open gates above for the receipt path, wall-clock bounds, store-count chain, and socket evidence. - Verdict: minimalness **9/10**, elegance **9/10**, correctness **9.5/10**. **Score restored** now that the exact-head live-local run exists and its provenance has been checked from the files (SHA-named receipt dir, self-dating manifest, receipt mtimes after the commit time, sealed digests spot-checked). Gatekeeper re-bless at `9a8128ba4`: **9/10, merge recommended.** The paired CPU benchmark remains the one gate not run. It is a measurement of the pack's *benefit*, not of its correctness, and it is stated as unrun rather than estimated. --------- Signed-off-by: Tyler Longwell <tlongwell@squareup.com> Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz> Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz> Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
1 parent 196d62f commit bbd20fa

42 files changed

Lines changed: 9751 additions & 3028 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

desktop/src-tauri/Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

desktop/src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ buzz_sdk_pkg = { package = "buzz-sdk", path = "../../crates/buzz-sdk" }
108108
buzz_agent_pkg = { package = "buzz-agent", path = "../../crates/buzz-agent" }
109109
buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" }
110110
buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" }
111+
buzz_ws_client_pkg = { package = "buzz-ws-client", path = "../../crates/buzz-ws-client" }
111112
portable-pty = "0.9"
112113
iroh = { version = "1.0.2", optional = true }
113114
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true }

desktop/src-tauri/src/archive/mod.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod metric_store;
2222
mod pipeline;
2323
pub mod store;
2424
mod store_migrations;
25+
pub mod sync;
2526

2627
use pipeline::{commit_archive, plan_archive, query_buckets};
2728

@@ -150,8 +151,20 @@ pub async fn archive_events(
150151
state: State<'_, AppState>,
151152
candidates: Vec<ArchiveCandidate>,
152153
) -> Result<ArchiveBatchResult, String> {
153-
let identity_pk = identity_pubkey(&state)?;
154-
let relay_url = relay_ws_url_with_override(&state);
154+
archive_candidates(&state, candidates).await
155+
}
156+
157+
/// The body of [`archive_events`], callable without a command invocation.
158+
///
159+
/// The native sync task archives through this directly: routing its batches
160+
/// back out to the renderer just to have the renderer invoke the command would
161+
/// reintroduce the IPC round trip the move exists to delete.
162+
pub(crate) async fn archive_candidates(
163+
state: &AppState,
164+
candidates: Vec<ArchiveCandidate>,
165+
) -> Result<ArchiveBatchResult, String> {
166+
let identity_pk = identity_pubkey(state)?;
167+
let relay_url = relay_ws_url_with_override(state);
155168
let now = now_secs();
156169

157170
// ── Phase 1: plan (blocking SQLite) ─────────────────────────────────────
@@ -163,8 +176,7 @@ pub async fn archive_events(
163176
.await?;
164177

165178
// ── Phase 2: relay queries (async) ───────────────────────────────────────
166-
let state_ref: &AppState = &state;
167-
let bucket_results = query_buckets(plan.buckets, state_ref).await;
179+
let bucket_results = query_buckets(plan.buckets, state).await;
168180

169181
// ── Phase 3: persist (blocking SQLite) ──────────────────────────────────
170182
let owner_keys = {
@@ -286,6 +298,7 @@ fn validate_ephemeral_frame(
286298
#[tauri::command]
287299
pub async fn create_save_subscription(
288300
state: State<'_, AppState>,
301+
sync_state: State<'_, sync::ArchiveSyncState>,
289302
scope_type: ScopeType,
290303
scope_value: String,
291304
kinds: Vec<u32>,
@@ -333,7 +346,9 @@ pub async fn create_save_subscription(
333346
&scope_value,
334347
&kinds_json,
335348
now,
336-
)
349+
)?;
350+
sync_state.notify_subscriptions_changed().await;
351+
Ok(())
337352
}
338353

339354
/// Probe: the current user has access to `channel_id` (kind 39002 lists them).
@@ -426,6 +441,7 @@ async fn probe_event_readable(state: &AppState, event_id: &str) -> Result<(), St
426441
#[tauri::command]
427442
pub async fn merge_save_subscription_kinds(
428443
state: State<'_, AppState>,
444+
sync_state: State<'_, sync::ArchiveSyncState>,
429445
kind: u32,
430446
) -> Result<(), String> {
431447
if kind > u32::from(u16::MAX) {
@@ -439,7 +455,9 @@ pub async fn merge_save_subscription_kinds(
439455
run_archive_db_task(move |conn| {
440456
store::merge_owner_p_kinds(conn, &identity_pk, &relay_url, &owner_pk, kind, now)
441457
})
442-
.await
458+
.await?;
459+
sync_state.notify_subscriptions_changed().await;
460+
Ok(())
443461
}
444462

445463
// ── remove_save_subscription_kind ────────────────────────────────────────────
@@ -459,6 +477,7 @@ pub async fn merge_save_subscription_kinds(
459477
#[tauri::command]
460478
pub async fn remove_save_subscription_kind(
461479
state: State<'_, AppState>,
480+
sync_state: State<'_, sync::ArchiveSyncState>,
462481
kind: u32,
463482
) -> Result<(), String> {
464483
if kind > u32::from(u16::MAX) {
@@ -471,7 +490,9 @@ pub async fn remove_save_subscription_kind(
471490
run_archive_db_task(move |conn| {
472491
store::remove_owner_p_kind(conn, &identity_pk, &relay_url, &owner_pk, kind)
473492
})
474-
.await
493+
.await?;
494+
sync_state.notify_subscriptions_changed().await;
495+
Ok(())
475496
}
476497

477498
// ── list_save_subscriptions ──────────────────────────────────────────────────
@@ -496,12 +517,13 @@ pub async fn list_save_subscriptions(
496517
#[tauri::command]
497518
pub async fn delete_save_subscription(
498519
state: State<'_, AppState>,
520+
sync_state: State<'_, sync::ArchiveSyncState>,
499521
scope_type: ScopeType,
500522
scope_value: String,
501523
) -> Result<bool, String> {
502524
let identity_pk = identity_pubkey(&state)?;
503525
let relay_url = relay_ws_url_with_override(&state);
504-
run_archive_db_task(move |conn| {
526+
let removed = run_archive_db_task(move |conn| {
505527
store::delete_save_subscription(
506528
conn,
507529
&identity_pk,
@@ -510,7 +532,11 @@ pub async fn delete_save_subscription(
510532
&scope_value,
511533
)
512534
})
513-
.await
535+
.await?;
536+
if removed {
537+
sync_state.notify_subscriptions_changed().await;
538+
}
539+
Ok(removed)
514540
}
515541

516542
// ── read_archived_events ─────────────────────────────────────────────────────

0 commit comments

Comments
 (0)