Skip to content

[P2][docs] Browser storage guidance: quotas, persist(), eviction, and the memory ceiling #63

Description

@cevheri

Summary

Roadmap item from real usage (the libredb.org playground runs LibreDB in browsers today). docs/BROWSER.md covers OPFS mechanics and mentions requesting persistence, but a user asking "how big can my database be in a browser, and will it survive?" has no single honest answer page. The facts are known and measured; write them down.

Content to add (verified 2026-07)

1. Quota table — OPFS is NOT localStorage

LibreDB's durable browser mode uses OPFS, which shares the Storage Standard quota pool with IndexedDB (the 5-10 MB localStorage folklore does not apply):

Browser Per-origin quota
Chrome/Edge ~60% of disk
Firefox ~10% of disk; ~50% with persist()
Safari/WebKit ~60% in-browser; ~15% for embedded WebViews

Runtime truth is navigator.storage.estimate() (a constrained headless Chromium profile still reported a 10 GB quota for libredb.org in testing). Reference: MDN "Storage quotas and eviction criteria".

2. Persistence and eviction — the part that loses data

  • Without navigator.storage.persist(), storage is best-effort: the browser may evict under disk pressure.
  • Safari ITP deletes ALL script-writable storage for an origin after 7 days without user interaction (home-screen web apps exempt). For a database this is the headline risk, above quota.
  • Recommend the persist() call in the Worker setup example, and show how to read navigator.storage.persisted().

3. Quota exhaustion maps to the failure latch

When an OPFS write hits the quota, the handle throws (QuotaExceededError) — the browser's ENOSPC. Since v0.2.0 a failed append/fsync latches the database (every later transact() throws FAILED until reopen), so behavior is already correct and documented in RELIABILITY.md; BROWSER.md should connect the dots explicitly ("what happens when I run out of quota").

4. The practical size ceiling is memory, not disk

The whole store lives in RAM and open() currently reads the whole WAL into one Uint8Array (engine ceilings: ~8 GiB on 64-bit, ~2 GiB on 32-bit; transient ~2x file size at open). Honest guidance: 100 MB comfortable everywhere; 500 MB desktop-fine; ~1 GB is the practical edge; 10 GB is out of reach today regardless of quota (same on Node — see the streaming-recovery issue and #12 for what changes this).

5. WAL growth vs quota

Append-only means quota is consumed by history, not live data, until compaction (#12) lands — worth one sentence with a link.

Acceptance criteria

  • BROWSER.md gains a "Storage limits and persistence" section covering the five points, with MDN links.
  • The Worker example includes navigator.storage.persist().
  • README's browser row links to it.
  • Docs only; no changeset.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions