2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-04 01:50:48 +00:00

checker: use channel of error instead of slice

This commit is contained in:
Alexander Neumann 2015-07-12 01:44:19 +02:00
parent 7e6174126f
commit af02c323cd
3 changed files with 73 additions and 33 deletions

View File

@ -192,28 +192,22 @@ outer:
} }
// Packs checks that all packs referenced in the index are still available and // Packs checks that all packs referenced in the index are still available and
// there are no packs that aren't in an index. // there are no packs that aren't in an index. errChan is closed after all
func (c *Checker) Packs() []error { // packs have been checked.
func (c *Checker) Packs(errChan chan<- error, done <-chan struct{}) {
defer close(errChan)
debug.Log("Checker.Packs", "checking for %d packs", len(c.packs)) debug.Log("Checker.Packs", "checking for %d packs", len(c.packs))
seenPacks := make(map[mapID]struct{}) seenPacks := make(map[mapID]struct{})
done := make(chan struct{})
defer close(done)
var workerWG sync.WaitGroup var workerWG sync.WaitGroup
IDChan := make(chan mapID) IDChan := make(chan mapID)
errChan := make(chan error)
for i := 0; i < defaultParallelism; i++ { for i := 0; i < defaultParallelism; i++ {
workerWG.Add(1) workerWG.Add(1)
go packIDTester(c.repo, IDChan, errChan, &workerWG, done) go packIDTester(c.repo, IDChan, errChan, &workerWG, done)
} }
errsChan := make(chan []error, 1)
go collectErrors(errChan, errsChan, done)
for id := range c.packs { for id := range c.packs {
seenPacks[id] = struct{}{} seenPacks[id] = struct{}{}
IDChan <- id IDChan <- id
@ -223,19 +217,17 @@ func (c *Checker) Packs() []error {
debug.Log("Checker.Packs", "waiting for %d workers to terminate", defaultParallelism) debug.Log("Checker.Packs", "waiting for %d workers to terminate", defaultParallelism)
workerWG.Wait() workerWG.Wait()
debug.Log("Checker.Packs", "workers terminated") debug.Log("Checker.Packs", "workers terminated")
close(errChan)
errs := <-errsChan
debug.Log("Checker.Packs", "error worker terminated")
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) debug.Log("Checker.Packs", "check data blob %v", id)
if _, ok := seenPacks[id2map(id)]; !ok { if _, ok := seenPacks[id2map(id)]; !ok {
errs = append(errs, PackError{id, errors.New("not referenced in any index")}) select {
case <-done:
return
case errChan <- PackError{id, errors.New("not referenced in any index")}:
}
} }
} }
return errs
} }
// Error is an error that occurred while checking a repository. // Error is an error that occurred while checking a repository.
@ -276,10 +268,9 @@ func loadTreeFromSnapshot(repo *repository.Repository, id backend.ID) (backend.I
} }
// Structure checks that for all snapshots all referenced blobs are available // Structure checks that for all snapshots all referenced blobs are available
// in the index. // in the index. errChan is closed after all trees have been traversed.
func (c *Checker) Structure() (errs []error) { func (c *Checker) Structure(errChan chan<- error, done <-chan struct{}) {
done := make(chan struct{}) defer close(errChan)
defer close(done)
var todo backend.IDs var todo backend.IDs
@ -288,7 +279,11 @@ func (c *Checker) Structure() (errs []error) {
treeID, err := loadTreeFromSnapshot(c.repo, id) treeID, err := loadTreeFromSnapshot(c.repo, id)
if err != nil { if err != nil {
errs = append(errs, err) select {
case <-done:
return
case errChan <- err:
}
continue continue
} }
@ -296,8 +291,13 @@ func (c *Checker) Structure() (errs []error) {
todo = append(todo, treeID) todo = append(todo, treeID)
} }
errs = append(errs, c.trees(todo)...) for _, err := range c.trees(todo) {
return errs select {
case <-done:
return
case errChan <- err:
}
}
} }
func (c *Checker) trees(treeIDs backend.IDs) (errs []error) { func (c *Checker) trees(treeIDs backend.IDs) (errs []error) {

View File

@ -24,14 +24,44 @@ func list(repo *repository.Repository, t backend.Type) (IDs []string) {
return IDs return IDs
} }
func checkPacks(chkr *checker.Checker) (errs []error) {
done := make(chan struct{})
defer close(done)
errChan := make(chan error)
go chkr.Packs(errChan, done)
for err := range errChan {
errs = append(errs, err)
}
return errs
}
func checkStruct(chkr *checker.Checker) (errs []error) {
done := make(chan struct{})
defer close(done)
errChan := make(chan error)
go chkr.Structure(errChan, done)
for err := range errChan {
errs = append(errs, err)
}
return errs
}
func TestCheckRepo(t *testing.T) { func TestCheckRepo(t *testing.T) {
WithTestEnvironment(t, checkerTestData, func(repodir string) { WithTestEnvironment(t, checkerTestData, func(repodir string) {
repo := OpenLocalRepo(t, repodir) repo := OpenLocalRepo(t, repodir)
chkr := checker.New(repo) chkr := checker.New(repo)
OK(t, chkr.LoadIndex()) OK(t, chkr.LoadIndex())
OKs(t, chkr.Packs()) OKs(t, checkPacks(chkr))
OKs(t, chkr.Structure()) OKs(t, checkStruct(chkr))
}) })
} }
@ -44,7 +74,7 @@ func TestMissingPack(t *testing.T) {
chkr := checker.New(repo) chkr := checker.New(repo)
OK(t, chkr.LoadIndex()) OK(t, chkr.LoadIndex())
errs := chkr.Packs() errs := checkPacks(chkr)
Assert(t, len(errs) == 1, Assert(t, len(errs) == 1,
"expected exactly one error, got %v", len(errs)) "expected exactly one error, got %v", len(errs))
@ -68,7 +98,7 @@ func TestUnreferencedPack(t *testing.T) {
chkr := checker.New(repo) chkr := checker.New(repo)
OK(t, chkr.LoadIndex()) OK(t, chkr.LoadIndex())
errs := chkr.Packs() errs := checkPacks(chkr)
Assert(t, len(errs) == 1, Assert(t, len(errs) == 1,
"expected exactly one error, got %v", len(errs)) "expected exactly one error, got %v", len(errs))
@ -101,8 +131,8 @@ func TestUnreferencedBlobs(t *testing.T) {
chkr := checker.New(repo) chkr := checker.New(repo)
OK(t, chkr.LoadIndex()) OK(t, chkr.LoadIndex())
OKs(t, chkr.Packs()) OKs(t, checkPacks(chkr))
OKs(t, chkr.Structure()) OKs(t, checkStruct(chkr))
blobs := chkr.UnusedBlobs() blobs := chkr.UnusedBlobs()
sort.Sort(blobs) sort.Sort(blobs)

View File

@ -52,15 +52,25 @@ func (cmd CmdCheck) Execute(args []string) error {
return err return err
} }
done := make(chan struct{})
defer close(done)
errorsFound := false errorsFound := false
errChan := make(chan error)
cmd.global.Verbosef("Check all packs\n") cmd.global.Verbosef("Check all packs\n")
for _, err := range checker.Packs() { go checker.Packs(errChan, done)
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") cmd.global.Verbosef("Check snapshots, trees and blobs\n")
for _, err := range checker.Structure() { errChan = make(chan error)
go checker.Structure(errChan, done)
for err := range errChan {
errorsFound = true errorsFound = true
fmt.Fprintf(os.Stderr, "error: %v\n", err) fmt.Fprintf(os.Stderr, "error: %v\n", err)
} }