Delete metadata directories a backup left without a manifest (closes #390)
check / check (push) Canceled after 0s

A backup that stopped between uploading db.zst.age and its manifest
left that directory under metadata/ on the destination for good:
prune deleted nothing there.

The blob step of prune, which snapshot create --prune also runs, now
deletes each directory under metadata/ that has no manifest.json.zst,
that no local snapshot record claims, and all of whose objects are
older than prune_grace_period; an object listed with no time keeps its
directory, and only names that are remote keys are deleted. It shares
prune's one listing of metadata/, follows the --force, prompt and
--json rules of unreferenced blobs, reports the count
(metadata_dirs_deleted, metadata_dirs_failed), and fails the run when
a delete fails.

Model: opus-5-5
This commit is contained in:
2026-10-14 21:26:39 +00:00
parent 6a57750a7b
commit ac71946170
5 changed files with 430 additions and 61 deletions
+15 -5
View File
@@ -453,22 +453,29 @@ may belong to a backup still running, on this host or another sharing the
destination store, that has not yet uploaded its manifest. The grace period
does not protect a blob such a backup reuses from an earlier run that stopped
before its manifest: that blob keeps the time of its first upload, so it can
be deleted. Step (3) reads all remote manifests — network cost scales with
the number of snapshots.
be deleted. Step (3) also deletes each directory under `metadata/` that has
no `manifest.json.zst`, as a backup leaves when it stops after uploading
`db.zst.age` but before its manifest, once every object in it is older than
`prune_grace_period` and its snapshot is not in the local index. An object the
listing gives no time for keeps its directory, and a directory whose name is
not a remote key is left alone. Step (3) reads all remote manifests — network
cost scales with the number of snapshots.
`snapshot create --prune` runs the same cleanup automatically; this is the
manual entry point for the same work. If the destination store cannot
be listed, or the local index holds snapshots and the destination has
no `vaultik-destination.txt` (an unplugged volume's empty mount point),
prune fails and deletes nothing. `snapshot purge` does the same. If a
blob cannot be deleted, the command (and `snapshot create --prune`)
exits non-zero.
blob or such a directory cannot be deleted, the command (and
`snapshot create --prune`) exits non-zero.
* `--force`: Skip confirmation prompt
* `--json`: Output stats as JSON. Also skips the confirmation prompt, as
`--force` does. A blob that cannot be deleted is counted in
`blobs_failed`, and the document is printed before the non-zero exit.
A blob kept for being newer than `prune_grace_period`, or because the
listing of the destination gives no time for it, is counted in
`blobs_too_new` and not in `blobs_found`.
`blobs_too_new` and not in `blobs_found`. A directory under `metadata/`
with no manifest is counted in `metadata_dirs_deleted` once deleted, or
in `metadata_dirs_failed` if it cannot be.
**`info`**: Display system configuration, storage settings, encryption
recipients, and local database statistics.
@@ -639,6 +646,9 @@ format does and does not protect.
3. List all blobs in storage
4. Delete any blob not in the referenced set that is older than
`prune_grace_period`
5. Delete each directory under `metadata/` with no manifest whose objects
are all older than `prune_grace_period`, unless its snapshot is in the
local index
### chunking and deduplication