@cryptotaxi247 / kubo / commits / f58276d6d

p2p/net/swarm: async grabbing of best conn

this is actually pretty important in case some peers end up changing addrs, so others dont fail dialing simply for picking the wrong addr to start with.

Juan Batiz-Benet committed Jan 13, 2015 at 08:13 UTC f58276d6d26e5f013de806ce679428d426c449af
1 file changed +69 -28
p2p/net/swarm/swarm_dial.go
+69 -28
@@ -294,42 +294,83 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
294 func (s *Swarm) dialAddrs(ctx context.Context, d *conn.Dialer, p peer.ID, remoteAddrs []ma.Multiaddr) (conn.Conn, error) {
295
296 // try to connect to one of the peer's known addresses.
297 - // for simplicity, we do this sequentially.
298 - // A future commit will do this asynchronously.
297 + // we dial concurrently to each of the addresses, which:
298 + // * makes the process faster overall
299 + // * attempts to get the fastest connection available.
300 + // * mitigates the waste of trying bad addresses
301 log.Debugf("%s swarm dialing %s %s", s.local, p, remoteAddrs)
300 - var err error
302 +
303 + ctx, cancel := context.WithCancel(ctx)
304 + defer cancel() // cancel work when we exit func
305 +
306 + foundConn := make(chan struct{})
307 + conns := make(chan conn.Conn, len(remoteAddrs))
308 + errs := make(chan error, len(remoteAddrs))
309 +
310 + //TODO: rate limiting just in case?
311 for _, addr := range remoteAddrs {
302 - log.Debugf("%s swarm dialing %s %s", s.local, p, addr)
303 - var connC conn.Conn
304 - connC, err = d.Dial(ctx, addr, p)
305 - if err != nil {
306 - log.Info("%s --> %s dial attempt failed: %s", s.local, p, err)
307 - continue
308 - }
312 + go func(addr ma.Multiaddr) {
313 + connC, err := s.dialAddr(ctx, d, p, addr)
314
310 - // if the connection is not to whom we thought it would be...
311 - if connC.RemotePeer() != p {
312 - log.Infof("misdial to %s through %s (got %s)", p, addr, connC.RemotePeer())
313 - connC.Close()
314 - continue
315 - }
315 + // check parent still wants our results
316 + select {
317 + case <-foundConn:
318 + if connC != nil {
319 + connC.Close()
320 + }
321 + return
322 + default:
323 + }
324
317 - // if the connection is to ourselves...
318 - // this can happen TONS when Loopback addrs are advertized.
319 - // (this should be caught by two checks above, but let's just make sure.)
320 - if connC.RemotePeer() == s.local {
321 - log.Infof("misdial to %s through %s", p, addr)
322 - connC.Close()
323 - continue
324 - }
325 + if err != nil {
326 + errs <- err
327 + } else if connC == nil {
328 + errs <- fmt.Errorf("failed to dial %s %s", p, addr)
329 + } else {
330 + conns <- connC
331 + }
332 + }(addr)
333 + }
334
326 - // success! we got one!
327 - return connC, nil
335 + err := fmt.Errorf("failed to dial %s", p)
336 + for i := 0; i < len(remoteAddrs); i++ {
337 + select {
338 + case err = <-errs:
339 + log.Info(err)
340 + case connC := <-conns:
341 + // take the first + return asap
342 + close(foundConn)
343 + return connC, nil
344 + }
345 }
346 + return nil, err
347 +}
348 +
349 +func (s *Swarm) dialAddr(ctx context.Context, d *conn.Dialer, p peer.ID, addr ma.Multiaddr) (conn.Conn, error) {
350 + log.Debugf("%s swarm dialing %s %s", s.local, p, addr)
351 +
352 + connC, err := d.Dial(ctx, addr, p)
353 if err != nil {
330 - return nil, err
354 + return nil, fmt.Errorf("%s --> %s dial attempt failed: %s", s.local, p, err)
355 }
332 - return nil, fmt.Errorf("failed to dial %s", p)
356 +
357 + // if the connection is not to whom we thought it would be...
358 + remotep := connC.RemotePeer()
359 + if remotep != p {
360 + connC.Close()
361 + return nil, fmt.Errorf("misdial to %s through %s (got %s)", p, addr, remotep)
362 + }
363 +
364 + // if the connection is to ourselves...
365 + // this can happen TONS when Loopback addrs are advertized.
366 + // (this should be caught by two checks above, but let's just make sure.)
367 + if remotep == s.local {
368 + connC.Close()
369 + return nil, fmt.Errorf("misdial to %s through %s (got self)", p, addr)
370 + }
371 +
372 + // success! we got one!
373 + return connC, nil
374 }
375
376 // dialConnSetup is the setup logic for a connection from the dial side. it