docs: RELEASES.md entry for 8.2.3 (transact durability barrier)

This commit is contained in:
David Snelling 2026-07-12 08:54:14 -07:00
parent 3b8fa51161
commit be5ce0bcc3

View file

@ -10,6 +10,34 @@ Full auto-generated changelog: `CHANGELOG.md` · Releases: https://github.com/so
---
## v8.2.3 — 2026-07-12 (a committed transaction is durable on return)
Durability fix. A `transact()` reported "committed" while its canonical entity writes were still
only in the OS page cache (written via tmp+rename, not yet `fsync`'d), even though the generation
counter and manifest WERE fsync'd. A hard kill (power loss, SIGKILL) in that window could leave the
durable generation counter **ahead of** the persisted entity bytes — so a consumer resuming from the
counter would see "phantom progress": a generation that claims writes the disk never kept. Reported
from a downstream migration's crash-lifecycle forensics.
`commitTransaction` now runs a **durability barrier**: it records every canonical write and delete
the batch's operations make, then `fsync`s that entire footprint (file contents, the rename
directory entries, and the parent directories of any deletes) **before** advancing the generation
counter and manifest. A committed transaction is therefore durable the moment `transact()` returns —
the counter can never outrun the entity bytes.
**Durability contract, now explicit.** `transact()` is durable-on-return (above). A **single-op**
write (`add`/`update`/`remove`/`relate`/…) is Model-B group-commit: its live bytes are written but
become durable at the next `flush()` or `close()`, not the instant the call resolves — the counter
is buffered alongside the data, so a crash loses both together (never a torn counter-ahead-of-state
store). Need per-write durability? Use `transact()` (even for one op), or `flush()` after the write.
This deliberately trades single-op fsync latency (a 3-5x write regression) for throughput.
No API change; no accelerator involvement (the barrier is in the filesystem storage adapter). In-memory
and durable-per-call (cloud object-PUT) adapters treat the barrier as a no-op. Regression:
`tests/integration/transact-durability-barrier.test.ts` proves entity writes fsync in an earlier
batch than the manifest, for single-op and multi-op (add+relate) transactions, and that a
precommit-rejected batch opens no barrier and advances nothing.
## v8.2.2 — 2026-07-11 (P0: a timed-out transaction now rolls back — no torn state)
Data-integrity fix. A transaction that exceeded its time budget **mid-flight** (e.g. a bulk