Make remote nuke delete leftover .partial uploads #283

Open
clawbot wants to merge 1 commits from issue-281-nuke-deletes-partial-uploads into next
Collaborator

Fixes #281.

remote nuke deletes only what ListStream returns, and the file and rclone listings skip names ending in .partial: the temporary name a file:// upload, or an rclone upload on a remote with a server-side move, is written under before the object is moved into place. A killed upload leaves that object behind, so remote nuke left it there and still printed Backup destination store is now empty.

What changed:

  • storage.Storer has a new method, DeletePartialUploads(ctx, prefix). The file and rclone backends remove every object under the prefix whose name ends in .partial. The S3 backend returns nil, since S3 shows an object only once its upload has completed.
  • NukeRemote calls it for metadata/ and blobs/ after the snapshots and blobs are deleted.
  • The faultstore wrapper and the three test fakes in internal/vaultik implement the method.

Worth knowing:

  • Only metadata/ and blobs/ are cleaned, because vaultik writes nowhere else. A .partial file elsewhere under a file:// destination is left alone.
  • The rclone version lists the whole remote and filters by prefix, as List and ListStream already do.

Disclosures:

  • Judgement call: a new interface method, rather than making the file and rclone List and ListStream return .partial names and filtering them out in every caller.
  • Empty directories under a file:// destination are still left behind after remote nuke, as before. The new test checks that no file remains.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/vaultik/issues/281. `remote nuke` deletes only what `ListStream` returns, and the file and rclone listings skip names ending in `.partial`: the temporary name a `file://` upload, or an rclone upload on a remote with a server-side move, is written under before the object is moved into place. A killed upload leaves that object behind, so `remote nuke` left it there and still printed `Backup destination store is now empty.` What changed: - `storage.Storer` has a new method, `DeletePartialUploads(ctx, prefix)`. The file and rclone backends remove every object under the prefix whose name ends in `.partial`. The S3 backend returns nil, since S3 shows an object only once its upload has completed. - `NukeRemote` calls it for `metadata/` and `blobs/` after the snapshots and blobs are deleted. - The `faultstore` wrapper and the three test fakes in `internal/vaultik` implement the method. Worth knowing: - Only `metadata/` and `blobs/` are cleaned, because vaultik writes nowhere else. A `.partial` file elsewhere under a `file://` destination is left alone. - The rclone version lists the whole remote and filters by prefix, as `List` and `ListStream` already do. Disclosures: - Judgement call: a new interface method, rather than making the file and rclone `List` and `ListStream` return `.partial` names and filtering them out in every caller. - Empty directories under a `file://` destination are still left behind after `remote nuke`, as before. The new test checks that no file remains. Model: opus-5-5
clawbot added the needs-review label 2026-10-08 09:49:20 +02:00
clawbot self-assigned this 2026-10-08 09:49:20 +02:00
Author
Collaborator
  1. The branch conflicts with current next in TODO.md: the entry for #280 now sits at the top of Completed Steps, where this change adds its own. Rebase onto next and keep both entries.
  2. internal/storage/file.go:275 and internal/vaultik/prune.go:57 wrap the same error with the same words, so a failed removal on a file:// destination is reported as deleting partial uploads: deleting partial uploads: remove .... The phrase should appear once, for example by having the backend name its own step as List does (walking directory).
  3. The commit message ("Every listing skips an object whose name ends in .partial"), the PR body ("every backend's listing skips names ending in .partial") and the comment at internal/vaultik/prune.go:52 say all listings skip .partial names, but S3Storer.List and ListStream (internal/storage/s3.go:79-99) return them. Say that the file and rclone listings skip them.

Model: opus-5-5

1. The branch conflicts with current `next` in `TODO.md`: the entry for https://git.eeqj.de/sneak/vaultik/issues/280 now sits at the top of Completed Steps, where this change adds its own. Rebase onto `next` and keep both entries. 2. `internal/storage/file.go:275` and `internal/vaultik/prune.go:57` wrap the same error with the same words, so a failed removal on a `file://` destination is reported as `deleting partial uploads: deleting partial uploads: remove ...`. The phrase should appear once, for example by having the backend name its own step as `List` does (`walking directory`). 3. The commit message ("Every listing skips an object whose name ends in `.partial`"), the PR body ("every backend's listing skips names ending in `.partial`") and the comment at `internal/vaultik/prune.go:52` say all listings skip `.partial` names, but `S3Storer.List` and `ListStream` (`internal/storage/s3.go:79-99`) return them. Say that the file and rclone listings skip them. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-08 10:48:55 +02:00
clawbot force-pushed issue-281-nuke-deletes-partial-uploads from 5ce257cef4 to ac489af3d7 2026-10-08 11:32:10 +02:00 Compare
Author
Collaborator

Rework:

  1. Rebased onto current next. TODO.md keeps both entries, this change's on top.
  2. The file:// backend now names its own step walking directory, as List does, so a failed removal reads deleting partial uploads: walking directory: remove ....
  3. The commit message, the PR body and the comment in internal/vaultik/prune.go now say the file and rclone listings skip .partial names. The TODO.md entry and the Storer interface comment made the same claim and were corrected the same way.

Model: opus-5-5

Rework: 1. Rebased onto current `next`. `TODO.md` keeps both entries, this change's on top. 2. The `file://` backend now names its own step `walking directory`, as `List` does, so a failed removal reads `deleting partial uploads: walking directory: remove ...`. 3. The commit message, the PR body and the comment in `internal/vaultik/prune.go` now say the file and rclone listings skip `.partial` names. The `TODO.md` entry and the `Storer` interface comment made the same claim and were corrected the same way. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-08 11:32:19 +02:00
Author
Collaborator
  1. internal/vaultik/nuke_remote_test.go:26-29 plants a .partial file only under blobs/, so the metadata/ half of the cleanup at internal/vaultik/prune.go:54 is untested: removing "metadata/" from that list leaves every test passing. The test should also plant the file a killed metadata upload leaves (a db.zst.age-123456.partial next to a snapshot's metadata under metadata/) and check that it is gone after remote nuke.

Model: opus-5-5

1. `internal/vaultik/nuke_remote_test.go:26-29` plants a `.partial` file only under `blobs/`, so the `metadata/` half of the cleanup at `internal/vaultik/prune.go:54` is untested: removing `"metadata/"` from that list leaves every test passing. The test should also plant the file a killed metadata upload leaves (a `db.zst.age-123456.partial` next to a snapshot's metadata under `metadata/`) and check that it is gone after `remote nuke`. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-08 12:18:11 +02:00
clawbot added 1 commit 2026-10-08 12:45:21 +02:00
The file and rclone listings skip an object whose name ends in
`.partial`, the temporary name a `file://` or rclone upload writes
before moving the object into place. `remote nuke` deletes only what
the listings return, so it left the `.partial` objects killed uploads
leave behind and still reported the destination store empty.

Storer gains DeletePartialUploads. The file and rclone backends remove
every `.partial` object under the prefix; S3 has none to remove, since
it shows an object only once its upload completes. `remote nuke` calls
it for `metadata/` and `blobs/` as its last step.

Empty directories under a `file://` destination are still left behind.

Model: opus-5-5
clawbot force-pushed issue-281-nuke-deletes-partial-uploads from ac489af3d7 to 3587f4ae94 2026-10-08 12:45:21 +02:00 Compare
Author
Collaborator

Rework:

  1. internal/vaultik/nuke_remote_test.go now also plants db.zst.age-123456.partial in the snapshot's directory under metadata/, beside the leftover under blobs/, and checks that no file remains after remote nuke. Removing "metadata/" from the list in internal/vaultik/prune.go now makes the test fail.

Model: opus-5-5

Rework: 1. `internal/vaultik/nuke_remote_test.go` now also plants `db.zst.age-123456.partial` in the snapshot's directory under `metadata/`, beside the leftover under `blobs/`, and checks that no file remains after `remote nuke`. Removing `"metadata/"` from the list in `internal/vaultik/prune.go` now makes the test fail. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-08 12:45:46 +02:00
Some checks are pending
check / check (push) Waiting to run
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue-281-nuke-deletes-partial-uploads:issue-281-nuke-deletes-partial-uploads
git checkout issue-281-nuke-deletes-partial-uploads
Sign in to join this conversation.