mirror of
https://github.com/octoleo/restic.git
synced 2024-12-23 11:28:54 +00:00
checker: add option to remove orphaned packs
This commit is contained in:
parent
5108d91bc7
commit
d36f07c6eb
@ -48,7 +48,8 @@ type Checker struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
M map[mapID]uint
|
M map[mapID]uint
|
||||||
}
|
}
|
||||||
indexes map[mapID]*repository.Index
|
indexes map[mapID]*repository.Index
|
||||||
|
orphanedPacks backend.IDs
|
||||||
|
|
||||||
masterIndex *repository.Index
|
masterIndex *repository.Index
|
||||||
|
|
||||||
@ -139,12 +140,13 @@ func (c *Checker) LoadIndex() error {
|
|||||||
|
|
||||||
// PackError describes an error with a specific pack.
|
// PackError describes an error with a specific pack.
|
||||||
type PackError struct {
|
type PackError struct {
|
||||||
ID backend.ID
|
ID backend.ID
|
||||||
error
|
Orphaned bool
|
||||||
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e PackError) Error() string {
|
func (e PackError) Error() string {
|
||||||
return "pack " + e.ID.String() + ": " + e.error.Error()
|
return "pack " + e.ID.String() + ": " + e.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func packIDTester(repo *repository.Repository, inChan <-chan mapID, errChan chan<- error, wg *sync.WaitGroup, done <-chan struct{}) {
|
func packIDTester(repo *repository.Repository, inChan <-chan mapID, errChan chan<- error, wg *sync.WaitGroup, done <-chan struct{}) {
|
||||||
@ -156,10 +158,10 @@ func packIDTester(repo *repository.Repository, inChan <-chan mapID, errChan chan
|
|||||||
for id := range inChan {
|
for id := range inChan {
|
||||||
ok, err := repo.Backend().Test(backend.Data, map2str(id))
|
ok, err := repo.Backend().Test(backend.Data, map2str(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = PackError{map2id(id), err}
|
err = PackError{ID: map2id(id), Err: err}
|
||||||
} else {
|
} else {
|
||||||
if !ok {
|
if !ok {
|
||||||
err = PackError{map2id(id), errors.New("does not exist")}
|
err = PackError{ID: map2id(id), Err: errors.New("does not exist")}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,10 +229,11 @@ func (c *Checker) Packs(errChan chan<- error, done <-chan struct{}) {
|
|||||||
for id := range c.repo.List(backend.Data, done) {
|
for id := range c.repo.List(backend.Data, done) {
|
||||||
debug.Log("Checker.Packs", "check data blob %v", id.Str())
|
debug.Log("Checker.Packs", "check data blob %v", id.Str())
|
||||||
if _, ok := seenPacks[id2map(id)]; !ok {
|
if _, ok := seenPacks[id2map(id)]; !ok {
|
||||||
|
c.orphanedPacks = append(c.orphanedPacks, id)
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
return
|
return
|
||||||
case errChan <- PackError{id, errors.New("not referenced in any index")}:
|
case errChan <- PackError{ID: id, Orphaned: true, Err: errors.New("not referenced in any index")}:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,3 +594,8 @@ func (c *Checker) UnusedBlobs() (blobs backend.IDs) {
|
|||||||
|
|
||||||
return blobs
|
return blobs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrphanedPacks returns a slice of unused packs (only available after Packs() was run).
|
||||||
|
func (c *Checker) OrphanedPacks() backend.IDs {
|
||||||
|
return c.orphanedPacks
|
||||||
|
}
|
||||||
|
@ -5,11 +5,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/restic/restic/backend"
|
||||||
"github.com/restic/restic/checker"
|
"github.com/restic/restic/checker"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CmdCheck struct {
|
type CmdCheck struct {
|
||||||
ReadData bool ` long:"read-data" description:"Read data blobs" default:"false"`
|
ReadData bool `long:"read-data" description:"Read data blobs" default:"false"`
|
||||||
|
RemoveOrphaned bool `long:"remove" description:"Remove data that isn't used" default:"false"`
|
||||||
|
|
||||||
global *GlobalOptions
|
global *GlobalOptions
|
||||||
}
|
}
|
||||||
@ -45,10 +47,10 @@ func (cmd CmdCheck) Execute(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
checker := checker.New(repo)
|
chkr := checker.New(repo)
|
||||||
|
|
||||||
cmd.global.Verbosef("Load indexes\n")
|
cmd.global.Verbosef("Load indexes\n")
|
||||||
if err = checker.LoadIndex(); err != nil {
|
if err = chkr.LoadIndex(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,20 +61,42 @@ func (cmd CmdCheck) Execute(args []string) error {
|
|||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
|
|
||||||
cmd.global.Verbosef("Check all packs\n")
|
cmd.global.Verbosef("Check all packs\n")
|
||||||
go checker.Packs(errChan, done)
|
go chkr.Packs(errChan, done)
|
||||||
|
|
||||||
|
foundOrphanedPacks := false
|
||||||
|
for err := range errChan {
|
||||||
|
errorsFound = true
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
|
||||||
|
if e, ok := err.(checker.PackError); ok && e.Orphaned {
|
||||||
|
foundOrphanedPacks = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.global.Verbosef("Check snapshots, trees and blobs\n")
|
||||||
|
errChan = make(chan error)
|
||||||
|
go chkr.Structure(errChan, done)
|
||||||
|
|
||||||
for err := range errChan {
|
for err := range errChan {
|
||||||
errorsFound = true
|
errorsFound = true
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.global.Verbosef("Check snapshots, trees and blobs\n")
|
for _, id := range chkr.UnusedBlobs() {
|
||||||
errChan = make(chan error)
|
cmd.global.Verbosef("unused blob %v\n", id.Str())
|
||||||
go checker.Structure(errChan, done)
|
}
|
||||||
|
|
||||||
for err := range errChan {
|
if foundOrphanedPacks && cmd.RemoveOrphaned {
|
||||||
errorsFound = true
|
IDs := chkr.OrphanedPacks()
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
cmd.global.Verbosef("Remove %d orphaned packs... ", len(IDs))
|
||||||
|
|
||||||
|
for _, id := range IDs {
|
||||||
|
if err := repo.Backend().Remove(backend.Data, id.String()); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.global.Verbosef("done\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if errorsFound {
|
if errorsFound {
|
||||||
|
Loading…
Reference in New Issue
Block a user