func testEnumerate(t *testing.T, sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) { var after string var n = 1000 for _, opt := range opts { switch v := opt.(type) { case string: after = v case int: n = v default: panic("bad option of type " + fmt.Sprint("%T", v)) } } want := append([]blob.SizedRef(nil), wantUnsorted...) sort.Sort(blob.SizedByRef(want)) sbc := make(chan blob.SizedRef, 10) var got []blob.SizedRef var grp syncutil.Group sawEnd := make(chan bool, 1) grp.Go(func() error { if err := sto.EnumerateBlobs(context.New(), sbc, after, n); err != nil { return fmt.Errorf("EnumerateBlobs(%q, %d): %v", after, n) } return nil }) grp.Go(func() error { for sb := range sbc { if !sb.Valid() { return fmt.Errorf("invalid blobref %#v received in enumerate", sb) } got = append(got, sb) } sawEnd <- true return nil }) grp.Go(func() error { select { case <-sawEnd: return nil case <-time.After(10 * time.Second): return errors.New("timeout waiting for EnumerateBlobs to close its channel") } }) if err := grp.Err(); err != nil { t.Fatalf("Enumerate error: %v", err) return } if len(got) == 0 && len(want) == 0 { return } if !reflect.DeepEqual(got, want) { t.Fatalf("Enumerate mismatch. Got %d; want %d.\n Got: %v\nWant: %v\n", len(got), len(want), got, want) } }
func enumerateAllBlobs(s blobserver.Storage, destc chan<- blobref.SizedBlobRef) error { // Use *client.Client's support for enumerating all blobs if // possible, since it could probably do a better job knowing // HTTP boundaries and such. if nh, ok := s.(noHub); ok { return nh.Client.SimpleEnumerateBlobs(destc) } const batchSize = 1000 defer close(destc) after := "" for { var wg sync.WaitGroup wg.Add(1) ch := make(chan blobref.SizedBlobRef) n := 0 go func() { defer wg.Done() for sb := range ch { after = sb.BlobRef.String() destc <- sb n++ } }() if err := s.EnumerateBlobs(ch, after, batchSize, 0); err != nil { return err } wg.Wait() if n == 0 { return nil } } }
func (sh *SyncHandler) runSync(srcName string, enumSrc blobserver.Storage, longPollWait time.Duration) int { if longPollWait != 0 { sh.setStatus("Idle; waiting for new blobs") // TODO: use longPollWait somehow. } enumch := make(chan blob.SizedRef) errch := make(chan error, 1) go func() { errch <- enumSrc.EnumerateBlobs(enumch, "", 1000) }() nCopied := 0 toCopy := 0 workch := make(chan blob.SizedRef, 1000) resch := make(chan copyResult, 8) for sb := range enumch { toCopy++ workch <- sb if toCopy <= sh.copierPoolSize { go sh.copyWorker(resch, workch) } sh.setStatus("Enumerating queued blobs: %d", toCopy) } close(workch) for i := 0; i < toCopy; i++ { sh.setStatus("Copied %d/%d of batch of queued blobs", nCopied, toCopy) res := <-resch // TODO(mpl): why is nCopied incremented while res.err hasn't been checked // yet? Maybe it should be renamed to nTried? nCopied++ sh.lk.Lock() if res.err == nil { sh.totalCopies++ sh.totalCopyBytes += res.sb.Size sh.recentCopyTime = time.Now().UTC() } else { sh.totalErrors++ } sh.lk.Unlock() } if err := <-errch; err != nil { sh.addErrorToLog(fmt.Errorf("replication error for source %q, enumerate from source: %v", srcName, err)) return nCopied } return nCopied }
func CheckEnumerate(sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) error { var after string var n = 1000 for _, opt := range opts { switch v := opt.(type) { case string: after = v case int: n = v default: panic("bad option of type " + fmt.Sprintf("%T", v)) } } want := append([]blob.SizedRef(nil), wantUnsorted...) sort.Sort(blob.SizedByRef(want)) sbc := make(chan blob.SizedRef, 10) var got []blob.SizedRef var grp syncutil.Group sawEnd := make(chan bool, 1) grp.Go(func() error { ctx := context.New() defer ctx.Cancel() if err := sto.EnumerateBlobs(ctx, sbc, after, n); err != nil { return fmt.Errorf("EnumerateBlobs(%q, %d): %v", after, n, err) } return nil }) grp.Go(func() error { var lastRef blob.Ref for sb := range sbc { if !sb.Valid() { return fmt.Errorf("invalid blobref %#v received in enumerate", sb) } got = append(got, sb) if lastRef.Valid() && sb.Ref.Less(lastRef) { return fmt.Errorf("blobs appearing out of order") } lastRef = sb.Ref } sawEnd <- true return nil }) grp.Go(func() error { select { case <-sawEnd: return nil case <-time.After(10 * time.Second): return errors.New("timeout waiting for EnumerateBlobs to close its channel") } }) if err := grp.Err(); err != nil { return fmt.Errorf("Enumerate error: %v", err) } if len(got) == 0 && len(want) == 0 { return nil } var gotSet = map[blob.SizedRef]bool{} for _, sb := range got { if gotSet[sb] { return fmt.Errorf("duplicate blob %v returned in enumerate", sb) } gotSet[sb] = true } if !reflect.DeepEqual(got, want) { return fmt.Errorf("Enumerate mismatch. Got %d; want %d.\n Got: %v\nWant: %v\n", len(got), len(want), got, want) } return nil }