-
v8.2.4 Stable
released this
2026-07-12 18:21:02 +02:00 | 69 commits to main since this releasev8.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 andENOSPCmid-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.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- The snapshot is copied into a staging area (