dht/bootstrap: (optional) parallelism + error on peer
This also makes it an Error to find a peer.
Juan Batiz-Benet committed
Dec 9, 2014 at 12:04 UTC
474b74f70b3fb583b2ccc2062a1acf3a86509cba
2 files changed
+59
-6
routing/dht/dht.go
+41
-6
@@ -342,6 +342,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) {
342
343
// Bootstrap builds up list of peers by requesting random peer IDs
344
func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error {
345
+ var merr u.MultiErr
346
347
randomID := func() peer.ID {
348
// 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 {
353
}
354
355
// bootstrap sequentially, as results will compound
355
- for i := 0; i < queries; i++ {
356
- id := randomID()
357
- log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i, queries, id)
356
+ runQuery := func(ctx context.Context, id peer.ID) {
357
p, err := dht.FindPeer(ctx, id)
358
if err == routing.ErrNotFound {
359
// this isn't an error. this is precisely what we expect.
360
} else if err != nil {
362
- log.Errorf("Bootstrap peer error: %s", err)
361
+ merr = append(merr, err)
362
} else {
364
- // woah, we got a peer under a random id? it _cannot_ be valid.
365
- log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", p)
363
+ // woah, actually found a peer with that ID? this shouldn't happen normally
364
+ // (as the ID we use is not a real ID). this is an odd error worth logging.
365
+ err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p)
366
+ log.Errorf("%s", err)
367
+ merr = append(merr, err)
368
}
369
}
370
+
371
+ sequential := true
372
+ if sequential {
373
+ // these should be parallel normally. but can make them sequential for debugging.
374
+ // note that the core/bootstrap context deadline should be extended too for that.
375
+ for i := 0; i < queries; i++ {
376
+ id := randomID()
377
+ log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id)
378
+ runQuery(ctx, id)
379
+ }
380
+
381
+ } else {
382
+ // note on parallelism here: the context is passed in to the queries, so they
383
+ // **should** exit when it exceeds, making this function exit on ctx cancel.
384
+ // normally, we should be selecting on ctx.Done() here too, but this gets
385
+ // complicated to do with WaitGroup, and doesnt wait for the children to exit.
386
+ var wg sync.WaitGroup
387
+ for i := 0; i < queries; i++ {
388
+ wg.Add(1)
389
+ go func() {
390
+ defer wg.Done()
391
+
392
+ id := randomID()
393
+ log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id)
394
+ runQuery(ctx, id)
395
+ }()
396
+ }
397
+ wg.Wait()
398
+ }
399
+
400
+ if len(merr) > 0 {
401
+ return merr
402
+ }
403
return nil
404
}
util/util.go
+18
@@ -126,3 +126,21 @@ func GetenvBool(name string) bool {
126
v := strings.ToLower(os.Getenv(name))
127
return v == "true" || v == "t" || v == "1"
128
}
129
+
130
+// multiErr is a util to return multiple errors
131
+type MultiErr []error
132
+
133
+func (m MultiErr) Error() string {
134
+ if len(m) == 0 {
135
+ return "no errors"
136
+ }
137
+
138
+ s := "Multiple errors: "
139
+ for i, e := range m {
140
+ if i != 0 {
141
+ s += ", "
142
+ }
143
+ s += e.Error()
144
+ }
145
+ return s
146
+}