func doPass(sc, dc *client.Client, passNum int) (stats SyncStats, retErr error) { srcBlobs := make(chan blobref.SizedBlobRef, 100) destBlobs := make(chan blobref.SizedBlobRef, 100) srcErr := make(chan error) destErr := make(chan error) go func() { srcErr <- sc.SimpleEnumerateBlobs(srcBlobs) }() checkSourceError := func() { if err := <-srcErr; err != nil { retErr = fmt.Errorf("Enumerate error from source: %v", err) } } if *flagDest == "stdout" { for sb := range srcBlobs { fmt.Printf("%s %d\n", sb.BlobRef, sb.Size) } checkSourceError() return } go func() { destErr <- dc.SimpleEnumerateBlobs(destBlobs) }() checkDestError := func() { if err := <-destErr; err != nil { retErr = errors.New(fmt.Sprintf("Enumerate error from destination: %v", err)) } } destNotHaveBlobs := make(chan blobref.SizedBlobRef) sizeMismatch := make(chan *blobref.BlobRef) readSrcBlobs := srcBlobs if *flagVerbose { readSrcBlobs = loggingBlobRefChannel(srcBlobs) } mismatches := []*blobref.BlobRef{} go client.ListMissingDestinationBlobs(destNotHaveBlobs, sizeMismatch, readSrcBlobs, destBlobs) For: for { select { case br := <-sizeMismatch: // TODO(bradfitz): check both sides and repair, carefully. For now, fail. log.Printf("WARNING: blobref %v has differing sizes on source and est", br) stats.ErrorCount++ mismatches = append(mismatches, br) case sb, ok := <-destNotHaveBlobs: if !ok { break For } fmt.Printf("Destination needs blob: %s\n", sb) blobReader, size, err := sc.FetchStreaming(sb.BlobRef) if err != nil { stats.ErrorCount++ log.Printf("Error fetching %s: %v", sb.BlobRef, err) continue } if size != sb.Size { stats.ErrorCount++ log.Printf("Source blobserver's enumerate size of %d for blob %s doesn't match its Get size of %d", sb.Size, sb.BlobRef, size) continue } uh := &client.UploadHandle{BlobRef: sb.BlobRef, Size: size, Contents: blobReader} pr, err := dc.Upload(uh) if err != nil { stats.ErrorCount++ log.Printf("Upload of %s to destination blobserver failed: %v", sb.BlobRef, err) continue } if !pr.Skipped { stats.BlobsCopied++ stats.BytesCopied += pr.Size } if *flagRemoveSource { if err = sc.RemoveBlob(sb.BlobRef); err != nil { stats.ErrorCount++ log.Printf("Failed to delete %s from source: %v", sb.BlobRef, err) } } } } checkSourceError() checkDestError() if retErr == nil && stats.ErrorCount > 0 { retErr = errors.New(fmt.Sprintf("%d errors during sync", stats.ErrorCount)) } return stats, retErr }