docs: RELEASES.md entry for 8.2.4 (non-destructive restore)
This commit is contained in:
parent
a2f4f6a550
commit
457469593a
1 changed files with 26 additions and 0 deletions
26
RELEASES.md
26
RELEASES.md
|
|
@ -10,6 +10,32 @@ Full auto-generated changelog: `CHANGELOG.md` · Releases: https://github.com/so
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## v8.2.4 — 2026-07-12 (restore can no longer destroy the store it's recovering)
|
||||||
|
|
||||||
|
Recovery-safety fix. `restore()` removed the entire live brain directory and THEN copied the
|
||||||
|
snapshot in — so any copy failure left the store destroyed with only a partial copy. The sharpest
|
||||||
|
edge: the copy (`fs.cp`) materialized the holes of sparse mmap blob files, so a snapshot that fits
|
||||||
|
on disk could balloon and `ENOSPC` mid-copy, and the recovery tool would have just destroyed the
|
||||||
|
brain it was asked to recover. Reported from a downstream incident's recovery forensics.
|
||||||
|
|
||||||
|
Restore is now **non-destructive and crash-resumable**:
|
||||||
|
|
||||||
|
- The snapshot is copied into a staging area (`_restore_staging/`) **before any live data is
|
||||||
|
touched**. The copy is **sparse-aware** — all-zero regions are left as holes, so a store of
|
||||||
|
mostly-hole blobs restores at its true allocated size instead of its apparent size.
|
||||||
|
- A copy failure (including `ENOSPC`) removes only the half-written staging area and throws; the
|
||||||
|
live store is left **exactly as it was**.
|
||||||
|
- Only after the copy succeeds and a completion marker is `fsync`'d does an **atomic per-entry
|
||||||
|
swap** move the staged data into place — same-filesystem renames that cannot fail for disk space.
|
||||||
|
- A crash mid-swap is finished **forward** on the next open: startup resumes a committed-but-
|
||||||
|
incomplete swap, or discards an uncommitted staging area (live data still authoritative).
|
||||||
|
|
||||||
|
No API change — `restore(path, { confirm: true })` is unchanged. `persist()` was already safe
|
||||||
|
(hard-link snapshot). Regression (`tests/integration/restore-nondestructive.test.ts`): a forced
|
||||||
|
copy failure leaves live data fully intact, a normal restore round-trips, an interrupted-but-
|
||||||
|
committed restore completes on reopen, an uncommitted staging area is discarded, and the sparse
|
||||||
|
copy is byte-identical with allocation far below apparent size.
|
||||||
|
|
||||||
## v8.2.3 — 2026-07-12 (a committed transaction is durable on return)
|
## 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
|
Durability fix. A `transact()` reported "committed" while its canonical entity writes were still
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue