Repository navigation
Web client: main thread saturated by synchronous JS (native mobile instant on same account/server) #1765
Description
Summary
On the web client, the UI becomes very sluggish (session list navigation and opening a session), while the native mobile app on the same account and the same self-hosted server is instant. A Chrome Performance profile shows the main thread is saturated by synchronous JS, not by rendering, network, or the server.
Environment
- Self-hosted server (
happy-server), web client served over HTTPS. - Chrome (latest) on Windows desktop.
- Native mobile app: same account, same server → fast. So the bottleneck is client-side in the web build, not the backend or the link.
Profile (Chrome DevTools → Performance, ~36 s capture: session-list scroll + opening one session)
Summary:
- Scripting 23,374 ms
- Rendering 111 ms
- Painting 79 ms
- System 4,283 ms
- Total 36,350 ms
→ Rendering/painting are negligible; it's ~64% Scripting. The main thread is blocked by JS, so the tab (and even DevTools itself) stutters.
Bottom-Up (Self time):
evaluate— 16,039 ms (61.3%)- Major GC — 1,312 ms (5.0%) ← heavy allocation churn
- Function call
index-0ed1099…js:1026:546— 707 ms self / 6,439 ms total (24.4%) (app hot path, minified) nextNode— 699 ms
The combination of a dominant evaluate self-time plus significant Major GC points at large synchronous work with high allocation — consistent with decrypting/parsing many sessions and re-deriving/re-sorting large in-memory structures on the main thread.
Ruling things out
- Extensions are not the cause: repeated in a clean Incognito window (extensions disabled) — identical slowness. In the profiled run, extension main-thread time was tiny (Bitwarden ~640 ms, everything else <100 ms), ~3% of Scripting.
- Not the server / network: the native app hits the same server and is instant.
Likely related
- Background history prefetch loads the entire session into memory, degrading long sessions on web #1453 — background prefetch loads the entire session history into memory, degrading long sessions on web.
- Prefetch latest messages for non-archived sessions in background (live first, others throttled) #1285 — prefetch latest messages for non-archived sessions.
- Laggy and unstable cross-device session switching between desktop and mobile #1170 — laggy cross-device session switching.
These describe specific prefetch paths; this report is about the web client's overall main-thread saturation (session list + session open), which may share the same root cause (whole datasets decrypted/parsed/sorted synchronously on the main thread).
Questions
- Is the session list and/or session open decrypting and parsing the full dataset synchronously on the main thread? Given
sessions.jsonon our machine is ~210 KB with many sessions, that would explain theevaluate+ Major GC signature. - Would offloading decrypt/parse to a Web Worker (or chunking +
requestIdleCallback) be acceptable, so the main thread stays responsive on web the way the native app already is?
Happy to capture a deeper trace (expanded call tree under index-…js:1026:546, an exported .json trace, or an unminified profile against a dev build) if that helps pinpoint the hot function.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsBacklog
Activity
perf(app): bound background-prefetch pages, don't grow session unbounded
perf(app): yield main thread periodically during libsodium decrypt
perf(hooks): drive useElapsedTime from a single shared ticker
perf(sync): decrypt and apply sessions in chunks, newest first
perf(sync): bound background history prefetch to a small page budget
perf(sync): bound background history prefetch across syncs