Repository navigation
[P3][kernel] Stream recovery record-by-record to bound open() memory #64
Copy link
Copy link
Open
Labels
area/kernelPre-announcement auditPre-announcement auditenhancementNew feature or requestNew feature or requestpriority/lowPre-announcement auditPre-announcement audit
Milestone
Description
Summary
Roadmap item. recover() currently reads the ENTIRE WAL into one Uint8Array (file.read(0, file.size())) and replays from that buffer. Two ceilings follow, both hit before any OS or browser storage quota does:
- A single-allocation ceiling: JS engines cap one ArrayBuffer/TypedArray at ~8 GiB on 64-bit (~2 GiB on 32-bit), so a file beyond that cannot even be opened.
- A transient ~2x memory cost at open: the file buffer and the parsed store coexist.
In browsers this is the binding constraint on database size (OPFS quotas are tens of GB in Chrome; the single-buffer read gives up far earlier) — measured and discussed in the browser-storage docs issue.
Why this is cheap architecturally
The WalFile seam ALREADY supports positional reads (read(offset, length)), and the v0.2.0 adapters honor it properly (node-fs does fd/offset reads; OPFS loops reads). recover() simply chooses to issue one whole-file read. Streaming is a recover()-local change:
- Read the 8-byte file header (or probe the legacy first record).
- Loop: read one 12-byte record header (validate its self-checksum), then read exactly the payload it promises, verify the payload CRC, replay, advance.
- Torn-tail / mid-log corruption classification carries over unchanged — the same decisions, made per-record instead of over a full buffer.
- A short read that is not end-of-file remains INCOMPLETE_READ.
Peak memory drops from (file size + store) to (largest single record + store).
Scope notes
- The whole-store-in-RAM model is NOT in question here (that is the documented design); this only removes the file-size buffer from open().
- Guarded-core change: keep recover() readable; measure open() time before/after on a large file (per-record read syscall overhead vs one big read — consider a modest fixed-size read chunk, e.g. 4-64 MB, as the middle ground if syscall cost shows).
- DST already exercises torn/corrupt/short-read paths; add a seed profile with reads served in chunks to pin the streaming logic.
- Pairs with Add WAL compaction to reclaim space and bound recovery time #12 (compaction bounds file size) and feeds the production-arc bar ([P3][docs] Define the production arc: the concrete bar from test/dev to production data #58).
Acceptance criteria
- open() peak memory no longer includes the whole file (demonstrated with a large-file test or instrumented fake fs).
- All existing recovery/DST tests green unchanged; new chunked-read DST profile.
- Changeset (minor) if observable; gate green.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area/kernelPre-announcement auditPre-announcement auditenhancementNew feature or requestNew feature or requestpriority/lowPre-announcement auditPre-announcement audit
Activity