perf(desktop): low-risk fixes from the canary performance review
HA
13h ago+158+158+012345678901234567890123456789-20-20-01234567890123456789
Links
- Review with evidence for each item: https://app.superset.sh/page/canary-performance-review-2-0-0-canary-20261029234-kcro28
Summary
Six small, independent fixes from the canary performance review, one commit each:
- React Query persister:
throttleTime20 s. It saved the whole persisted cache about once a second while idle. - Login-shell env (host-service): one shared in-flight spawn, a 15 min TTL, and rc-file stderr logged once per process. Before, concurrent callers each spawned their own
zsh -i -l -c, and a real log had 7,462 spawns. - xterm image addon:
storageLimit: 16(MB) andpixelLimit: 2048². The defaults were 128 MB and 4096² per terminal, and up to 12 parked terminals stay alive. See Known Limitations: open question. - SQLite:
synchronous = NORMALafter WAL in the chat-runtime, host-service and desktop local DBs. It was FULL, so every commit did an fsync. - app.asar:
filesexcludes*.map,*.d.ts,*.d.mtsand*.d.cts. 418 MB of the 1.13 GB asar is maps. - Auto-updater: download progress is logged at 25/50/75/100% only. It was 66% of a real
main.log.
How It Works
- Shell env: the cache is now
createShellEnvCache(resolveEnv, now, ttlMs), so tests can inject the spawn function and the clock. A result that arrives afterclear()is not cached, and a failed spawn clears the in-flight promise. Callers still get a copy. - Updater: a small
createDownloadProgressMilestones()helper. It resets onupdate-available, which electron-updater emits before each download (autoDownloadis the only download path). - Merge with main: #9149 had already set
throttleTime: 10_000. I kept its comment and used the 20 s from the review. Reviewers can choose either value.
Manual QA (before/after on a cloud dev box, same route and signed-in org)
- Persister, running app, 2 min idle: 74 → 6 IndexedDB cache writes, 10.35 → 0.84 MB written. Renderer CPU did not change (6.5/18.2% → 6.6/19.3% script/task). This account's persisted cache is small (137 KB, about 4 ms per save), so the CPU gain for large PR/issue caches is not measured.
- Shell env, real zsh and real module: 8 concurrent calls went from 8 spawns and 8 stderr logs to 1 and 1. With a failing shell, the next call spawns again, so the failure is not cached.
- Image addon, the app's
loadAddons()on a real xterm withscrollback: 5000, 100 × 1024² inline images: 126.5 → 14.6 MB held. A 3000² image is now dropped. This was a synthetic check on a test terminal, not a terminal opened through the UI. - SQLite: both
createChatDband the host-servicecreateDbreportsynchronous = 1(NORMAL). 2,000 single-row commits went from 122 to 20 µs each on xfs. - Updater: replaying 4,367 progress events gives 4 log lines. It logs again after a reset.
- app.asar size: not tested. It needs a packaged build.
Source maps (fix 5)
- Sentry still gets the maps. In
build-desktop.yml,compile:appruns the vite build withSENTRY_AUTH_TOKEN, and the Sentry vite plugin uploads during that step, before the separatebun run packagestep. - No runtime reader. Nothing loads
.mapor.d.tsfiles at runtime: nosource-map-support, no--enable-source-maps, and workerexecArgvis unset in production. - Partial coverage. Top-level
!patterns apply todist/and to thenode_modulesthat electron-builder collects. They do not apply to the object-form copies inruntime-dependencies.ts(native modules), so those still ship their maps.
Testing
bun run typecheckinapps/desktop,packages/host-serviceandpackages/chat-runtime: passbun run lint(root): passbun test:apps/desktop: 8,981 pass, 0 failpackages/chat-runtime: 430 pass, 0 failpackages/host-service: 2,709 pass, 1 fail. The failure ishealth.test.ts, which reads the cloud box's real/opt/superset/bundle/current.bundle-hash. This PR does not touch that code.
- New tests:
clean-shell-env.test.ts(dedupe, failure not cached, TTL) anddownload-progress-milestones.test.ts.
Known Limitations
- Image limits may be too tight (open).
- Images larger than 2048² (for example a 2880×1800 Retina screenshot shown with
imgcator kitty) are now dropped without a placeholder. storageLimit: 16is 4,000,000 pixels, which is smaller than one image at the newpixelLimit. So one large image evicts all older ones.- A safer pair is roughly
pixelLimit3840×2400 andstorageLimit64.
- Images larger than 2048² (for example a 2880×1800 Retina screenshot shown with
- Slower rc updates for git/gh. With the 15 min TTL,
git/ghcalls can use the old login-shell env for up to 15 min after a user edits their rc files. Terminals take their env once at startup and are not affected. - Persister CPU unproven. The persister still builds a cache snapshot on every cache event before the throttle. If idle CPU stays high, profile that next.
Summary by cubic
Picks six low-risk performance fixes from the canary review, targeting desktop idle CPU, shell spawn volume, SQLite write latency, packaged app size, and log volume.
- The React Query IndexedDB persister now throttles to 20 s (was 10 s after a merge), cutting idle cache writes from about once a second to once per 20 s.
getStrictShellEnvironmentnow shares a single in-flightzsh -i -l -cspawn per process, keeps results for 15 minutes instead of 60 s, and logs rc-file stderr once per process instead of per spawn.- The xterm image addon now caps storage at 16 MB and pixels at 2048² per terminal; a real log previously had 7,462 concurrent shell spawns.
- The chat-runtime, host-service, and desktop-local SQLite DBs now use
synchronous = NORMALwith WAL, avoiding an fsync per commit. - The packaged
app.asarexcludes*.map,*.d.ts,*.d.mts, and*.d.cts, removing about 445 MB; Sentry still receives source maps because they upload during the build before packaging. - Auto-updater download progress logs only at 25/50/75/100% instead of on every chunk event.
Known limitations
- Images larger than 2048² (for example a Retina screenshot) are dropped without a placeholder, and one max-size image exceeds the 16 MB storage limit so it evicts all older images.
gitandghcalls can use a login-shell env up to 15 minutes stale after rc-file edits; terminals take their env once at startup and are unaffected.
Summary by CodeRabbit
- Bug Fixes
- Improved terminal reliability by reusing shell environment results and refreshing them periodically; failed lookups can be retried.
- Limited terminal image rendering to help prevent excessive resource use.
- Performance
- Updated local data storage settings and reduced the frequency of cache saves.
- Other Improvements
- Update download progress is logged at key milestones, while in-app progress updates keep their existing cadence.