From 474b74f70b3fb583b2ccc2062a1acf3a86509cba Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 9 Dec 2014 12:04:25 -0800 Subject: [PATCH] dht/bootstrap: (optional) parallelism + error on peer This also makes it an Error to find a peer. --- routing/dht/dht.go | 47 ++++++++++++++++++++++++++++++++++++++++------ util/util.go | 18 ++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4d87ebbd8..d6a6d8770 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -342,6 +342,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { // Bootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { + var merr u.MultiErr randomID := func() peer.ID { // 16 random bytes is not a valid peer id. it may be fine becuase @@ -352,18 +353,52 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { } // bootstrap sequentially, as results will compound - for i := 0; i < queries; i++ { - id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i, queries, id) + runQuery := func(ctx context.Context, id peer.ID) { p, err := dht.FindPeer(ctx, id) if err == routing.ErrNotFound { // this isn't an error. this is precisely what we expect. } else if err != nil { - log.Errorf("Bootstrap peer error: %s", err) + merr = append(merr, err) } else { - // woah, we got a peer under a random id? it _cannot_ be valid. - log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", p) + // woah, actually found a peer with that ID? this shouldn't happen normally + // (as the ID we use is not a real ID). this is an odd error worth logging. + err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) + log.Errorf("%s", err) + merr = append(merr, err) } } + + sequential := true + if sequential { + // these should be parallel normally. but can make them sequential for debugging. + // note that the core/bootstrap context deadline should be extended too for that. + for i := 0; i < queries; i++ { + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + runQuery(ctx, id) + } + + } else { + // note on parallelism here: the context is passed in to the queries, so they + // **should** exit when it exceeds, making this function exit on ctx cancel. + // normally, we should be selecting on ctx.Done() here too, but this gets + // complicated to do with WaitGroup, and doesnt wait for the children to exit. + var wg sync.WaitGroup + for i := 0; i < queries; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + runQuery(ctx, id) + }() + } + wg.Wait() + } + + if len(merr) > 0 { + return merr + } return nil } diff --git a/util/util.go b/util/util.go index 32bc314ab..611c988ed 100644 --- a/util/util.go +++ b/util/util.go @@ -126,3 +126,21 @@ func GetenvBool(name string) bool { v := strings.ToLower(os.Getenv(name)) return v == "true" || v == "t" || v == "1" } + +// multiErr is a util to return multiple errors +type MultiErr []error + +func (m MultiErr) Error() string { + if len(m) == 0 { + return "no errors" + } + + s := "Multiple errors: " + for i, e := range m { + if i != 0 { + s += ", " + } + s += e.Error() + } + return s +}