2
2
mirror of https://github.com/octoleo/restic.git synced 2024-12-23 03:18:55 +00:00

Merge pull request #3139 from aawsome/prune-healing

prune: Add healing of repository in some situations
This commit is contained in:
MichaelEischer 2020-12-29 22:17:27 +01:00 committed by GitHub
commit e1efc193e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 6 deletions

View File

@ -245,7 +245,7 @@ func prune(opts PruneOptions, gopts GlobalOptions, repo restic.Repository, usedB
// Check if all used blobs have been found in index // Check if all used blobs have been found in index
if len(usedBlobs) != 0 { if len(usedBlobs) != 0 {
Warnf("%v not found in the new index\n"+ Warnf("%v not found in the index\n"+
"Data blobs seem to be missing, aborting prune to prevent further data loss!\n"+ "Data blobs seem to be missing, aborting prune to prevent further data loss!\n"+
"Please report this error (along with the output of the 'prune' run) at\n"+ "Please report this error (along with the output of the 'prune' run) at\n"+
"https://github.com/restic/restic/issues/new/choose", usedBlobs) "https://github.com/restic/restic/issues/new/choose", usedBlobs)
@ -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)
} }

View File

@ -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) {

Binary file not shown.