Skip to content

sessionStorage is ~85% full on a normal load and cache writes fail silently #153

Description

@2ndtlmining

Summary

sessionStorage runs at roughly 85% of quota on a normal home-page load, and every write is
wrapped in a silent try {} catch {}. When it tips over, caching stops working with no error,
no log and no user-visible symptom other than the app refetching several megabytes on every
page load.

Measured on the live site's production build:

Key Size
globalPerfRankings_v3 2,912 KB
homeAppSpecs_v2 1,408 KB
homeGeoCounts_v1 3 KB
gpuPrices_v1 2 KB
Total 4,325 KB

Browser sessionStorage quota is typically ~5,120 KB per origin. Writing another 1.3 MB throws
QuotaExceededError immediately.

How it was found

While bumping homeAppSpecs_v1_v2 in #148. The bump left the old ~1.3 MB entry behind,
which was enough to push the new write past the quota. homeAppSpecs_v2 was silently never
written — the page rendered correctly and refetched every time, with nothing in the console.
It only became visible because the cache key was being explicitly checked.

That orphan is fixed in #148, but it exposed the underlying problem: there is no headroom.

Why it matters

  • Two keys account for 4.3 MB and both grow with the network. Flux is at ~6,500 nodes and
    ~1,120 app specs today; neither figure is shrinking.
  • Any future cache key bump repeats the same silent failure for the lifetime of the tab.
  • Users on browsers with a smaller quota, or with other tabs on the same origin, lose caching
    entirely and refetch ~4 MB per navigation.
  • Because the catch is silent, nobody finds out.

Suggested directions

Roughly in order of value for effort.

1. Do not cache what can be recomputed. homeAppSpecs stores rawSpecs — the entire
globalappsspecifications response — but the consumers need a small derived subset. #148 took
this approach for fluxinfo: it caches a ~365-entry aggregate rather than the ~465 KB payload,
and recomputes categories on read. The same applies here.

2. Trim globalPerfRankings_v3. At 2.9 MB it is the single biggest consumer. Worth checking
whether the whole ranking set is needed client-side or only the top N plus the searched wallet.

3. Make the failure loud. Replace the silent catch with something that at minimum
console.warns on QuotaExceededError, and consider evicting the largest stale entry and
retrying once.

4. Consider IndexedDB for the large payloads. localforage is already a dependency and is
already used for appStore. IndexedDB quotas are orders of magnitude larger and it is async,
which suits payloads of this size better than a synchronous multi-megabyte
JSON.stringify on the main thread.

5. Add a size guard. Refuse to write entries over a threshold rather than attempting and
silently failing, so the behaviour is deliberate.

Acceptance criteria

  • Normal home-page load stays under ~50% of the sessionStorage quota
  • A quota failure is logged rather than swallowed
  • Cache key bumps do not leave orphans (a generic prune, not a per-key list)
  • Caching still works after the change — verified by checking the keys are actually present
    after a load, not just assumed

Reproduction

// on https://fluxnode.app.runonflux.io/#/home, after the page settles
Object.keys(sessionStorage).map(k => ({ key: k, kb: Math.round(sessionStorage.getItem(k).length / 1024) }))
try { sessionStorage.setItem('__probe', 'x'.repeat(1.3 * 1024 * 1024)) } catch (e) { e.name }
// -> "QuotaExceededError"

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions