forked from TrueCloudLab/restic
prune: Add self-healing
Allow prune to heal situations where blobs in the index are missing or the corresponding packfiles are damaged if those blobs are not needed.
This commit is contained in:
parent
cb6b0f6255
commit
f6df94a50e
3 changed files with 39 additions and 5 deletions
|
@ -310,7 +310,11 @@ func prune(opts PruneOptions, gopts GlobalOptions, repo restic.Repository, usedB
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.unusedSize+p.usedSize != uint64(packSize) {
|
if p.unusedSize+p.usedSize != uint64(packSize) &&
|
||||||
|
!(p.usedBlobs == 0 && p.duplicateBlobs == 0) {
|
||||||
|
// Pack size does not fit and pack is needed => error
|
||||||
|
// If the pack is not needed, this is no error, the pack can
|
||||||
|
// and will be simply removed, see below.
|
||||||
Warnf("pack %s: calculated size %d does not match real size %d\nRun 'restic rebuild-index'.",
|
Warnf("pack %s: calculated size %d does not match real size %d\nRun 'restic rebuild-index'.",
|
||||||
id.Str(), p.unusedSize+p.usedSize, packSize)
|
id.Str(), p.unusedSize+p.usedSize, packSize)
|
||||||
return errorSizeNotMatching
|
return errorSizeNotMatching
|
||||||
|
@ -356,10 +360,26 @@ func prune(opts PruneOptions, gopts GlobalOptions, repo restic.Repository, usedB
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At this point indexPacks contains only missing packs!
|
||||||
|
|
||||||
|
// missing packs that are not needed can be ignored
|
||||||
|
ignorePacks := restic.NewIDSet()
|
||||||
|
for id, p := range indexPack {
|
||||||
|
if p.usedBlobs == 0 && p.duplicateBlobs == 0 {
|
||||||
|
ignorePacks.Insert(id)
|
||||||
|
stats.blobs.remove += p.unusedBlobs
|
||||||
|
stats.size.remove += p.unusedSize
|
||||||
|
delete(indexPack, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(indexPack) != 0 {
|
if len(indexPack) != 0 {
|
||||||
Warnf("The index references pack files which are missing from the repository: %v\n", indexPack)
|
Warnf("The index references needed pack files which are missing from the repository: %v\n", indexPack)
|
||||||
return errorPacksMissing
|
return errorPacksMissing
|
||||||
}
|
}
|
||||||
|
if len(ignorePacks) != 0 {
|
||||||
|
Verbosef("missing but unneded pack files are referenced in the index, will be repaired\n")
|
||||||
|
}
|
||||||
|
|
||||||
repackAllPacksWithDuplicates := true
|
repackAllPacksWithDuplicates := true
|
||||||
|
|
||||||
|
@ -492,12 +512,20 @@ func prune(opts PruneOptions, gopts GlobalOptions, repo restic.Repository, usedB
|
||||||
removePacks.Merge(repackPacks)
|
removePacks.Merge(repackPacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(removePacks) != 0 {
|
if len(ignorePacks) == 0 {
|
||||||
err = rebuildIndexFiles(gopts, repo, removePacks, nil)
|
ignorePacks = removePacks
|
||||||
|
} else {
|
||||||
|
ignorePacks.Merge(removePacks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ignorePacks) != 0 {
|
||||||
|
err = rebuildIndexFiles(gopts, repo, ignorePacks, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(removePacks) != 0 {
|
||||||
Verbosef("removing %d old packs\n", len(removePacks))
|
Verbosef("removing %d old packs\n", len(removePacks))
|
||||||
DeleteFiles(gopts, repo, removePacks, restic.PackFile)
|
DeleteFiles(gopts, repo, removePacks, restic.PackFile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1502,10 +1502,16 @@ func TestEdgeCaseRepos(t *testing.T) {
|
||||||
|
|
||||||
// repo where a blob is missing
|
// repo where a blob is missing
|
||||||
// => check and prune should fail
|
// => check and prune should fail
|
||||||
t.Run("no-data", func(t *testing.T) {
|
t.Run("missing-data", func(t *testing.T) {
|
||||||
testEdgeCaseRepo(t, "repo-data-missing.tar.gz", opts, pruneDefaultOptions, false, false)
|
testEdgeCaseRepo(t, "repo-data-missing.tar.gz", opts, pruneDefaultOptions, false, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// repo where blobs which are not needed are missing or in invalid pack files
|
||||||
|
// => check should fail and prune should repair this
|
||||||
|
t.Run("missing-unused-data", func(t *testing.T) {
|
||||||
|
testEdgeCaseRepo(t, "repo-unused-data-missing.tar.gz", opts, pruneDefaultOptions, false, true)
|
||||||
|
})
|
||||||
|
|
||||||
// repo where data exists that is not referenced
|
// repo where data exists that is not referenced
|
||||||
// => check and prune should fully work
|
// => check and prune should fully work
|
||||||
t.Run("unreferenced-data", func(t *testing.T) {
|
t.Run("unreferenced-data", func(t *testing.T) {
|
||||||
|
|
BIN
cmd/restic/testdata/repo-unused-data-missing.tar.gz
vendored
Normal file
BIN
cmd/restic/testdata/repo-unused-data-missing.tar.gz
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue