Skip to content

perf(desktop): move observer-feed archive and decrypt commands off main thread - #3415

Merged
wpfleger96 merged 1 commit into
mainfrom
wpfleger96/observer-feed-async-commands
Jul 29, 2026
Merged

perf(desktop): move observer-feed archive and decrypt commands off main thread#3415
wpfleger96 merged 1 commit into
mainfrom
wpfleger96/observer-feed-async-commands

Conversation

@wpfleger96

Copy link
Copy Markdown
Member

Opening the agent observer feed could beachball the app. In Tauri 2, a sync (pub fn) command body runs on the main thread — only async fn commands run on the runtime pool. Five commands on the observer-feed open path were sync, so panel open ran SQLite I/O and secp256k1 work on the macOS main thread:

Command Main-thread work
decrypt_observer_event Schnorr ID + signature verify, then NIP-44 decrypt — once per frame
read_archived_observer_events_for_channel Opens the archive DB, runs the channel-index JOIN, returns up to 200 raw JSON blobs per page
read_unindexed_observer_rows Opens the DB, returns all not-yet-indexed kind-24200 rows in one shot
index_observer_channel_id Opens the DB, loops N upserts
delete_save_subscription Opens the DB, one delete

Eager hydration loads up to 10 pages × 200 frames on panel open, so that's up to 10 main-thread DB reads plus up to 2,000 sequential verify+decrypt calls before any scrolling. The one-shot backfill makes it worse on the first open after history accumulates: one read of every unindexed row, a decrypt per row, then a batch upsert — all on the main thread, and all proportional to archive size.

The four archive commands now route their DB work through the existing run_archive_db_task helper (spawn_blocking + open_db), matching list_save_subscriptions, read_archived_events, and archive_events directly around them. decrypt_observer_event becomes async fn + tauri::async_runtime::spawn_blocking, with state.signing_keys() extracted before the spawn since State is not Send — the same pattern sign_event uses from #1222.

No frontend changes: invoke is already promise-based, so the TS wrappers in tauriArchive.ts and tauriObserver.ts are unchanged.

This removes the freeze, not the work. Eager hydration still takes the same wall time — the feed shows a loading state instead of blocking the UI. Batching the per-frame decrypt IPC (2,000 round-trips into one command) would cut the latency itself; that's deliberately out of scope here.

…in thread

In Tauri 2 a sync command body runs on the main thread; only async commands
run on the runtime pool. The observer-feed open path invoked five sync
commands, so opening the panel ran up to ten SQLite page reads plus one
Schnorr verify + NIP-44 decrypt per frame on the macOS main thread, which
beachballs the app — worst on the first open after history accumulates,
when the one-shot backfill reads every unindexed row.

Routes the four archive commands through the existing run_archive_db_task
helper and wraps decrypt_observer_event in spawn_blocking, matching the
pattern established for the crypto commands in #1222. Wall time for eager
hydration is unchanged; the UI now shows a loading state instead of
freezing.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 requested a review from a team as a code owner July 28, 2026 22:16
@wpfleger96
wpfleger96 merged commit 294c8c8 into main Jul 29, 2026
25 checks passed
@wpfleger96
wpfleger96 deleted the wpfleger96/observer-feed-async-commands branch July 29, 2026 15:17
wesbillman added a commit that referenced this pull request Jul 29, 2026
## Buzz Desktop release v0.5.1

### Changes since v0.5.0:

- perf(desktop): move observer-feed archive and decrypt commands off
main thread ([#3415](#3415))
([`294c8c821`](294c8c8))
- fix(desktop): preserve shared agent fidelity
([#3553](#3553))
([`f7a3988ba`](f7a3988))
- feat(agent): route Claude/GPT model families to their native gateway
wire ([#3538](#3538))
([`6438dedf8`](6438ded))
- Refine community invite limits
([#3529](#3529))
([`24d90d128`](24d90d1))
- feat(agent): fix Anthropic prompt caching with Databricks (+ MCP
proxy/TLS passthrough)
([#3463](#3463))
([`c405ad1d4`](c405ad1))
- feat: add explicit entry for claude-opus-5 in model config
([#2831](#2831))
([`90e058ebf`](90e058e))
- fix(desktop): clear stale thread new-message pill
([#3411](#3411))
([`55a3ed7b9`](55a3ed7))
- fix(ci): ratchet file sizes against the base tree
([#3352](#3352))
([`9227bdf58`](9227bdf))
- feat(desktop): apply WebKit rendering workarounds at startup on Linux
([#3271](#3271))
([`3ece4461d`](3ece446))
- fix(desktop): stabilize flaky DM expansion E2E ordering assertions
([#2004](#2004))
([`913d564ce`](913d564))
- fix(desktop): paint community rail full height
([#3382](#3382))
([`1d3b810ad`](1d3b810))
- feat(desktop): add custom harness inline from agent dialogs
([#3252](#3252))
([`b0503d80c`](b0503d8))
- feat(desktop): refine agent catalog sharing
([#2439](#2439))
([`a35771fc4`](a35771f))
- fix(desktop): keep drafts out of the Inbox All view
([#3217](#3217))
([`3afa129ee`](3afa129))
- fix(desktop): restore the inbox icon in the sidebar
([#3341](#3341))
([`00ede2e7a`](00ede2e))
- fix(desktop): gate codex-acp on a minimum supported version
([#3254](#3254))
([`4e3998f36`](4e3998f))
- feat(cli): add users set-status command for NIP-38 profile status
([#3253](#3253))
([`60158fce3`](60158fc))
- fix(composer): scope multiline block formatting
([#3246](#3246))
([`5457c947a`](5457c94))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
joahg added a commit to joahg/buzz that referenced this pull request Jul 29, 2026
…-style

* origin/main:
  chore(release): release Buzz Desktop version 0.5.1 (block#3566)
  Run Tauri clippy in pre-push (block#3555)
  perf(desktop): move observer-feed archive and decrypt commands off main thread (block#3415)
  fix(desktop): preserve shared agent fidelity (block#3553)
  Polish mobile navigation and menus (block#3486)
  feat(agent): route Claude/GPT model families to their native gateway wire (block#3538)
  Refine community invite limits (block#3529)
  Polish mobile typing indicator (block#3528)

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
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.

2 participants