fix(renderer): the infinite loop due to which the app ui stays frozen

Closed
SN
3mo ago
+1076-55

What & why

Refs #5496 — fixes the freeze, but does not close the issue (remaining items listed below).

When localStorage is full, the desktop app becomes permanently unusable: UI frozen, renderer pegged at 100%+ CPU, RSS climbing past 5 GB. Restarting the app or the machine doesn't help, because the full store is on disk. The only escape today is manually deleting ~/Library/Application Support/Superset/Partitions/superset/Local Storage.

localStorageCollectionOptions re-serializes the whole collection on every mutation and rethrows QuotaExceededError. The library treats that throw as a failed transaction and rolls the optimistic write back → the live query re-emits → the effect that queued the write retries → repeat, ~40x a second, with zero user interaction.

The fix guards the storage boundary rather than one call site. On quota error: reclaim orphaned terminal snapshots, retry once, then return without throwing. No throw means no rollback, so the loop cannot form at any of the ~15 sites that write to these collections — not just usePlaceLocalWorktreesInSidebar.

It also reclaims orphaned terminal-buffer:* / terminal-dims:* keys — snapshots for terminals that no longer exist. Eviction leaves those behind by design (terminal-runtime-registry.ts) and nothing ever collected them; the reporter found 528 such keys, ~8.5 MB. Reclaim runs on the quota path rather than at startup, so a profile left wedged by a previous session recovers on next launch instead of needing Finder.

Follows existing conventions in this directory: withQuotaGuard is a sibling of withReadHeal, and the pure-policy/side-effect split mirrors selectRuntimesToEvict. No library patch needed — localStorageCollectionOptions already accepts an injectable storage.

Deliberately out of scope (from the issue's other defects):

  • No cap on terminal-buffer:* for terminals still open (issue suggestis, so reclaim skips them — with enough live terminals there's nothing tofree and writes fail permanently. Degraded rather than frozen, but still permanent. Happy to take this as an immediate follow-up.
  • Tombstone rows, uncapped viewedFiles, base64 initialFiles in `paner growth contributors, untouched.
  • Still one key per org (issue suggestion 3). Neutralized, not restructured — that's a persistence-format migration and I'd want a maintainer's preference first, since the
    existing SQLite helper is built for Electric-synced collections rather t

User-facing notice on genuine exhaustion is behind QUOTA_NOTICE_MODE i— currentlysilent (console warning only, matching howterminal-runtime-registryalready handles this error).notifyandoffer-reclaim` are implemented alongside it, so switching is a one-word change if you'd prefer a
toast.

How I tested it

Reproduced on unmodified main first, then replayed the identical six:

BeforeAfter (same wedged profile)
Renderer CPU115%2%
RSS1.4 GB136 MB
Quota errors / 40s1,5990
Main-thread eval RTTtimed out ×3p50 0.8 ms, no timeouts
Orphaned snapshot keys4130
Workspace rows persisted0 (all rolled back)12

Repro: local stack via setup.local.sh, throwaway repo imported as a project, 12 worktree workspaces, localStorage filled to the real ceiling, then let sidebar placement run. Measured over CDP against this workspace's matched renderer. Beforeeenshotand evenRuntime.enable` timed out. Killing every process andrelaunching cold re-wedged within a minute — confirming the "restart doesn't help" report.
One correction to the issue: the quota was ~49 MB on Electron 40, not ~10 MB. Different threshold, identical mechanism.
Tests: 27 new unit tests across the four new modules. The regression test was verified to fail against unguarded code (the rollback is asynchronous, so asserting synchronously passes even when broken). Mutation-tested six deliberate bskip reclaim, swallow non-quota errors, break warn dedupe, ignore mode,leak the live Set — each killed a test.

clearAllTerminalSnapshots has an explicit test asserting it never touches collection data.

Full desktop suite: 2293 pass. The 8 failures on my machine are pre-existing and environmental (runTeardown + getProcessEnvWithShellPath fail when $SHELL is nushell,
since getCommandShellArgs falls back to -lc) — verified identical wiling separately.

Checklist

  • PR title follows conventional commits (type(scope): subject)
  • bun run lint and bun run typecheck pass (CI fails on lint warnings too)
  • "Allow edits from maintainers" is checked on fork PRs

Open in Stage

Summary by cubic

Fixes the freeze caused by an infinite retry loop when localStorage hits quota. Writes now fail soft: we reclaim safe terminal snapshots, retry once, then drop the write so the UI stays responsive and data stays in memory for the session.

  • Bug Fixes
    • Wrapped all localStorage-backed collections with withQuotaGuard: on QuotaExceededError, reclaim, retry once, then return (no throw → no rollback loop across writers).
    • Reclaims only orphaned terminal snapshot keys via TERMINAL_BUFFER_KEY_PREFIX/TERMINAL_DIMS_KEY_PREFIX; parked and released-but-restorable terminals are preserved via the registry’s getRegisteredTerminalIds, and dispose clears preserved records.
    • Added notifyQuotaExhausted behind QUOTA_NOTICE_MODE (silent by default); “offer-reclaim” shows a toast with “Free up space” that clears saved scrollback and reports cleared entries (entries, not terminals).
    • Terminal runtime now uses shared key prefixes; reclaim runs on the quota path so wedged profiles recover on next launch; tests cover guard behavior, reclaim selection, notices, and preservation/cleanup paths.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented app freezes when browser storage quota is hit by safely handling failed persistence writes and avoiding retry loops.
    • Improved terminal state restore by keeping “parked” scrollback and dimensions available, and correctly reflecting which terminals have restorable data.
    • Automatically reclaims obsolete/unreachable terminal snapshots to make room for new saved state.
  • User Experience

    • Added clearer handling for storage-capacity issues: configurable warning toasts, message deduping, and an option to free saved terminal scrollback so changes can persist.