Delete metadata directories a backup left without a manifest (closes #390)
check / check (push) Waiting to run
check / check (push) Waiting to run
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. The prompt and the --force output name each such directory, the --json result counts them (metadata_dirs_deleted, metadata_dirs_failed), and a failed delete fails the run. Every description of the grace period now covers these directories. Model: opus-5-5
This commit is contained in:
@@ -98,11 +98,12 @@ vaultik snapshot restore <snapshot-id> ~/vaultik-restore
|
||||
|
||||
On a destination shared between hosts or users, the prune in that cron line
|
||||
relies on `prune_grace_period` (default `24h`) to leave the blobs of a backup
|
||||
another one is still running, so it must be longer than the longest backup
|
||||
run. It does not protect a blob that backup reuses from an earlier run that
|
||||
stopped before its manifest: the prune can delete such a blob, and the new
|
||||
snapshot then cannot be restored. To not depend on the grace period, give each
|
||||
host its own prefix or directory. See [locking](#locking).
|
||||
another one is still running, and its directory under `metadata/` that has no
|
||||
manifest yet, so it must be longer than the longest backup run. It does not
|
||||
protect a blob that backup reuses from an earlier run that stopped before its
|
||||
manifest: the prune can delete such a blob, and the new snapshot then cannot
|
||||
be restored. To not depend on the grace period, give each host its own prefix
|
||||
or directory. See [locking](#locking).
|
||||
|
||||
## restoring on another machine
|
||||
|
||||
@@ -204,18 +205,19 @@ corrupt the local index.
|
||||
|
||||
The lock covers one user on one machine. It does not stop another user,
|
||||
or another host, from writing to the same destination store at the same
|
||||
time. A backup uploads its blobs before the manifest that lists them, so a
|
||||
prune from elsewhere at that moment finds blobs no snapshot lists. It
|
||||
deletes such a blob only once it is older than `prune_grace_period`
|
||||
(default `24h`), which therefore must be longer than the longest backup run
|
||||
of any host or user sharing the destination. The grace period does not
|
||||
protect a blob a backup reuses from an earlier run that stopped before its
|
||||
manifest: a backup skips the upload of a blob already on the destination, so
|
||||
that blob keeps the time of its first upload, and a prune from elsewhere
|
||||
during the backup can delete it; the new snapshot then cannot be restored.
|
||||
`remote nuke` deletes everything regardless. To not depend on the grace
|
||||
period, give each host or user its own prefix (or directory) on the
|
||||
destination.
|
||||
time. A backup uploads its blobs, and then its `db.zst.age`, before the
|
||||
manifest that lists them, so a prune from elsewhere at that moment finds
|
||||
blobs no snapshot lists and a directory under `metadata/` with no manifest. It
|
||||
deletes such a blob only once it is older than `prune_grace_period` (default
|
||||
`24h`), and such a directory only once every object in it is, so the grace
|
||||
period must be longer than the longest backup run of any host or user sharing
|
||||
the destination. The grace period does not protect a blob a backup reuses
|
||||
from an earlier run that stopped before its manifest: a backup skips the
|
||||
upload of a blob already on the destination, so that blob keeps the time of
|
||||
its first upload, and a prune from elsewhere during the backup can delete it;
|
||||
the new snapshot then cannot be restored. `remote nuke` deletes everything
|
||||
regardless. To not depend on the grace period, give each host or user its own
|
||||
prefix (or directory) on the destination.
|
||||
|
||||
Read-only commands — `info`, `snapshot list`, `snapshot verify`, and
|
||||
`remote info` — do not take the lock and are never blocked, so they run
|
||||
@@ -318,9 +320,12 @@ value where a list belongs or a word where a number belongs), is refused.
|
||||
error that explains how to fix it
|
||||
* `--cron`: Silent on total success; warnings and errors are still printed
|
||||
(for crontab)
|
||||
* `--prune`: After backup, drop older snapshots of each backed-up name and
|
||||
* `--prune`: After backup, drop older snapshots of each backed-up name,
|
||||
remove orphaned blobs older than `prune_grace_period` from remote storage,
|
||||
as `prune` does. By default keeps only the latest
|
||||
and delete each directory under `metadata/` that has no
|
||||
`manifest.json.zst` once every object in it is older than
|
||||
`prune_grace_period` and its snapshot is not in the local index, as `prune`
|
||||
does. By default keeps only the latest
|
||||
snapshot per name; use `--keep-newer-than` for a rolling window. When
|
||||
`--skip-errors` skipped a file or directory it could not read or list, the
|
||||
prune does not run and every older snapshot is kept, since one of them may
|
||||
@@ -453,22 +458,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.
|
||||
@@ -545,8 +557,9 @@ and can be deleted.
|
||||
|
||||
A destination shared between hosts or users, each with its own local index,
|
||||
relies on `prune_grace_period` to keep one's prune from deleting the blobs of
|
||||
another's backup still running, so it must be longer than the longest backup
|
||||
run. It does not protect a blob that backup reuses from an earlier run that
|
||||
another's backup still running, or its directory under `metadata/` that has
|
||||
no manifest yet, so it must be longer than the longest backup run. It does not
|
||||
protect a blob that backup reuses from an earlier run that
|
||||
stopped before its manifest, which the prune can delete. To not depend on the
|
||||
grace period, give each host its own prefix (or directory). See
|
||||
[locking](#locking).
|
||||
@@ -639,6 +652,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
|
||||
|
||||
@@ -683,7 +699,7 @@ complete annotated example also lives in
|
||||
| `chunk_size` | `10MB` | Average chunk size for content-defined chunking |
|
||||
| `blob_size_limit` | `10GB` | Maximum blob size before splitting. Must be at least four times `chunk_size` (the largest chunk the chunker can emit), otherwise a single-chunk blob could exceed the limit. A backup needs free temporary space, because each blob is written whole to a temporary file before it is uploaded (up to about `blob_size_limit`; an rclone destination that cannot stream uploads needs about twice that) and the metadata export writes copies of the local index. Temporary files go to `$TMPDIR` (default `/tmp`); with `TMPDIR` unset, SQLite writes one of those copies to `/var/tmp` |
|
||||
| `compression_level` | `3` | zstd compression level (1-19) |
|
||||
| `prune_grace_period` | `24h` | How old a blob that no snapshot lists must be before `prune` deletes it. A backup uploads its blobs before the manifest that lists them, so on a destination shared between hosts or users this must be longer than the longest backup run (see [locking](#locking)). It does not protect a blob a backup reuses from an earlier run that stopped before its manifest: that blob keeps the time of its first upload, so a prune can delete it. A duration such as `36h` or `90m`; `0s` deletes such blobs at once |
|
||||
| `prune_grace_period` | `24h` | How old a blob that no snapshot lists must be before `prune` deletes it, and how old every object in a directory under `metadata/` with no manifest must be before `prune` deletes that directory. A backup uploads its blobs and its `db.zst.age` before the manifest that lists them, so on a destination shared between hosts or users this must be longer than the longest backup run (see [locking](#locking)). It does not protect a blob a backup reuses from an earlier run that stopped before its manifest: that blob keeps the time of its first upload, so a prune can delete it. A duration such as `36h` or `90m`; `0s` deletes such blobs and directories at once |
|
||||
| `hostname` | system hostname | Hostname used in snapshot IDs |
|
||||
| `index_path` | platform data dir | Local SQLite index path |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user