-
v0.6.1
StableCI / shell (shellcheck + syntax) (push) Successful in 9sCI / python 3.11 (push) Successful in 13sCI / python 3.13 (push) Successful in 16sCI / python 3.12 (push) Successful in 16sTrueNAS compatibility / compat (push) Failing after 6sRelease / release (push) Successful in 16sreleased this
2026-07-13 16:42:22 -04:00 | 28 commits to main since this releaseFixed
-
A reboot mid-backup orphaned the entire snapshot tree, permanently. The sidecar
is the record of which snapshots a run pinned — and it lives in/run, which is
tmpfs. A reboot (or a crash) between taking the recursive snapshot and cleaning
it up destroyed that record, leaving one snapshot per descendant dataset — 250+ on
a real pool — with nothing left pointing at them. Nothing would ever have found
them again.gc_stale_snapshots()is the backstop: it identifies leftovers by name, so it
works when the record is gone. It runs at the start of every backup, after the
sidecar reclaim — the recorded path stays authoritative, and the collector only ever
mops up what the record lost.Because it deletes data on a name match — a weaker claim than a recorded fact — the
selection is a pure function with the harshest tests in the suite. A snapshot is
collected only if all of these hold:name is exactly <dataset>@<task>-<YYYYMMDDHHMMSS>so cloud_backup-5never matchescloud_backup-50, anauto-*periodic snapshot, or anything a human madeit is not the current run's parent and children are excluded nothing is mounted from it an in-flight run pins its own snapshots — this, not the age guard, is what protects a concurrent backup it is over an hour old covers the seconds-long window where a live run has snapshotted but not yet mounted Verified against the real pool: of 4,728 snapshots — including 2,341 periodic
ones — it selects exactly the orphans of the task being run, and nothing else.
Downloads
-