Motivation: when listing pack files fails during prune (for example due
to a request timeout), the error was returned unwrapped and main printed
it with "%+v", which dumps a stack trace. As discussed in the issue, the
error needs one more level of wrapping before it can be treated as a
fatal error.
Approach: in decidePackAction, wrap an error from listing pack files
with errors.Fatalf("failed to list pack files: %v"). Errors that are
already fatal (e.g. ErrSizeNotMatching) and errors after context
cancellation are returned unchanged. The underlying error is preserved
via Unwrap. Retry log messages are unchanged; only the final error
output is shortened and says where the failure occurred.
Validation: `go test ./internal/repository/ -run Prune` and
`go test ./cmd/restic/ -run Prune` pass. The new test
TestPrunePackListError (backend whose pack list fails) fails without
the change and passes with it. I did not reproduce a real REST backend
timeout.
Report: https://github.com/restic/restic/issues/22120
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Assisted-by: claude-sonnet-5-5 (via Claude Code)
- addToPacks now returns uint32 directly and the value is passed through
store() to indexMap.add unchanged, instead of casting between int and
uint32.
- merge() appends idx2.packs to idx.packs without going through
addToPacks, so it now checks the pack count against the uint32 limit
itself + error if exceeded.
- optimize changelog message
Each blob in a repository requires one index entry in memory, which is one
of the largest contributors to restic's memory usage. The indexEntry.packIndex
field, an index into the per-Index packs slice, was stored as a machine-word
int (8 bytes on amd64) although the value is always a small array index.
Store it as a uint32 instead. This shrinks indexEntry from 64 to 56 bytes on
64-bit platforms (56 % 8 == 0, so no padding is added), reducing index memory
by roughly 12% -- about 100 MB for a repository with ~13 million blobs, as
confirmed by profiling. uint32 allows 4.29 billion packs (>30 billion blobs at
the assumed 8 blobs/pack), far beyond any real repository; addToPacks guards
against overflow defensively. The on-disk repository format is unaffected.
The treeCache in SnapshotsDir was never cleared when snapshots were
reloaded. This caused the "latest" symlink to keep pointing to the
previous snapshot even after new snapshots were added.
Add a generation counter to SnapshotsDirStructure that is incremented
whenever the directory structure is rebuilt (in makeDirs). The
treeCache checks this generation on each lookup and resets itself
when the generation changes, ensuring cached nodes (including symlinks)
are refreshed after a snapshot reload.
The treeCache in SnapshotsDir was never cleared when snapshots were
reloaded. This caused the "latest" symlink to keep pointing to the
previous snapshot even after new snapshots were added.
Add a generation counter to SnapshotsDirStructure that is incremented
whenever the directory structure is rebuilt (in makeDirs). The
treeCache checks this generation on each lookup and resets itself
when the generation changes, ensuring cached nodes (including symlinks)
are refreshed after a snapshot reload.
The SFTP backend created repository directories with pkg/sftp's Mkdir
and MkdirAll, which take no mode argument, so the directories inherited
the SFTP server's umask instead of the 0700 used for local
repositories. Set the mode of each directory the backend creates.