Skip to content

[P1][adapters] Loop OPFS reads; fail recovery on short read instead of truncating #23

Description

@cevheri

Summary

recover() treats any short read (fewer bytes than file.size()) as a torn tail and truncates. The OPFS adapter's read() performs one handle.read() call and does not loop to fill the buffer — short reads are legal per the OPFS API. A transient short read at reopen can destroy fsync'd commits. The DST short-read test currently asserts this data loss as acceptable — that expectation must flip.

Audit ID: B10 (High)
Wave: 2 (adapter + recovery; pairs with #10 OPFS E2E)

Root cause

OPFS adapter:

read(offset, length) {
  const buffer = new Uint8Array(length);
  const read = handle.read(buffer, { at: offset });
  return buffer.subarray(0, read);
}

Kernel recovery:

const log = file.read(0, file.size());
// ...
if (end > log.length) break; // treats short read as torn

Append already loops (opfs.ts:64-67); read does not.

Proposed fix

1. OPFS adapter — loop reads (mirror append)

read(offset, length) {
  const buffer = new Uint8Array(length);
  let filled = 0;
  while (filled < length) {
    const n = handle.read(buffer.subarray(filled), { at: offset + filled });
    if (n === 0) break;
    filled += n;
  }
  return buffer.subarray(0, filled);
}

2. Kernel recover() — fail loud on short read

If log.length !== file.size() after read → throw (libredb: incomplete WAL read) instead of treating as torn tail.

Distinguish:

  • read returned < size → IO/read error (throw)
  • read returned == size but last record incomplete → torn tail (truncate)

3. Fix DST short-read test

Update src/sim/dst.ts / SimFS armShortRead expectations: short read during full-file read should error, not truncate commits.

Related issues

Acceptance criteria

  • OPFS read loops until length or EOF.
  • Recovery throws on size mismatch after read.
  • DST short-read profile updated.
  • bun run gate green.
  • Changeset required.

Agent constraints

  • node-fs reads whole file today (readFileSync) — size mismatch unlikely but kernel check still valuable.

Verification

bun run gate

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions