From 67d2dcbf6deef5d3e12148cd4f49c240b46b48e8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 1 Apr 2019 12:34:01 +0300 Subject: [PATCH 1/6] parallelize dialbacks --- autonat.go | 94 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/autonat.go b/autonat.go index 410d733..7897ff7 100644 --- a/autonat.go +++ b/autonat.go @@ -32,7 +32,7 @@ var ( AutoNATBootDelay = 15 * time.Second AutoNATRetryInterval = 90 * time.Second AutoNATRefreshInterval = 15 * time.Minute - AutoNATRequestTimeout = 60 * time.Second + AutoNATRequestTimeout = 30 * time.Second ) // AutoNAT is the interface for ambient NAT autodiscovery @@ -135,50 +135,74 @@ func (as *AmbientAutoNAT) autodetect() { } cli := NewAutoNATClient(as.host, as.getAddrs) - failures := 0 - - for _, pi := range peers { - ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) - as.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL) - a, err := cli.DialBack(ctx, pi.ID) - cancel() - - switch { - case err == nil: - log.Debugf("NAT status is public; address through %s: %s", pi.ID.Pretty(), a.String()) - as.mx.Lock() - as.addr = a - as.status = NATStatusPublic - as.confidence = 0 - as.mx.Unlock() - return + ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) + defer cancel() + + var mx sync.Mutex + var pubaddr ma.Multiaddr + private := 0 + public := 0 - case IsDialError(err): - log.Debugf("dial error through %s: %s", pi.ID.Pretty(), err.Error()) - failures++ - if failures >= 3 || as.confidence >= 3 { // 3 times is enemy action - log.Debugf("NAT status is private") - as.mx.Lock() - as.status = NATStatusPrivate - as.confidence = 3 - as.mx.Unlock() - return + probe := 3 - as.confidence + if probe == 0 { + probe = 1 + } + if probe > len(peers) { + probe = len(peers) + } + + var wg sync.WaitGroup + + for _, pi := range peers[:probe] { + wg.Add(1) + go func(pi pstore.PeerInfo) { + as.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL) + a, err := cli.DialBack(ctx, pi.ID) + + switch { + case err == nil: + log.Debugf("Dialback through %s successful; public address is %s", pi.ID.Pretty(), a.String()) + mx.Lock() + public++ + pubaddr = a + mx.Unlock() + + case IsDialError(err): + log.Debugf("Dialback through %s failed", pi.ID.Pretty()) + mx.Lock() + private++ + mx.Unlock() + + default: + log.Debugf("Dialback error through %s: %s", pi.ID.Pretty(), err) } - default: - log.Debugf("Error dialing through %s: %s", pi.ID.Pretty(), err.Error()) - } + wg.Done() + }(pi) } + wg.Wait() + as.mx.Lock() - if failures > 0 { - as.status = NATStatusPrivate - as.confidence++ + if public > 0 { + log.Debugf("NAT status is public") + as.status = NATStatusPublic + as.addr = pubaddr + if as.confidence < 3 { + as.confidence++ + } + } else if private > 0 { log.Debugf("NAT status is private") + as.status = NATStatusPrivate + as.addr = nil + if as.confidence < 3 { + as.confidence++ + } } else { + log.Debugf("NAT status is unknown") as.status = NATStatusUnknown + as.addr = nil as.confidence = 0 - log.Debugf("NAT status is unknown") } as.mx.Unlock() } From 88875a785a813b19bb07bd6e75ac87cb0de72bc7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 1 Apr 2019 12:39:39 +0300 Subject: [PATCH 2/6] reset confidence when there is a public<->private transition --- autonat.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/autonat.go b/autonat.go index 7897ff7..2bc5fd0 100644 --- a/autonat.go +++ b/autonat.go @@ -186,18 +186,22 @@ func (as *AmbientAutoNAT) autodetect() { as.mx.Lock() if public > 0 { log.Debugf("NAT status is public") - as.status = NATStatusPublic - as.addr = pubaddr - if as.confidence < 3 { + if as.status == NATStatusPrivate { + as.confidence = 0 + } else if as.confidence < 3 { as.confidence++ } + as.status = NATStatusPublic + as.addr = pubaddr } else if private > 0 { log.Debugf("NAT status is private") - as.status = NATStatusPrivate - as.addr = nil - if as.confidence < 3 { + if as.status == NATStatusPublic { + as.confidence = 0 + } else if as.confidence < 3 { as.confidence++ } + as.status = NATStatusPrivate + as.addr = nil } else { log.Debugf("NAT status is unknown") as.status = NATStatusUnknown From de44e8118a87c3bde97bad74291379dc7d9b33ec Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 2 Apr 2019 20:15:25 +0300 Subject: [PATCH 3/6] use anonymous struct for collecting dialback results --- autonat.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/autonat.go b/autonat.go index 2bc5fd0..46d949d 100644 --- a/autonat.go +++ b/autonat.go @@ -138,10 +138,12 @@ func (as *AmbientAutoNAT) autodetect() { ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) defer cancel() - var mx sync.Mutex - var pubaddr ma.Multiaddr - private := 0 - public := 0 + var result struct { + sync.Mutex + private int + public int + pubaddr ma.Multiaddr + } probe := 3 - as.confidence if probe == 0 { @@ -162,16 +164,16 @@ func (as *AmbientAutoNAT) autodetect() { switch { case err == nil: log.Debugf("Dialback through %s successful; public address is %s", pi.ID.Pretty(), a.String()) - mx.Lock() - public++ - pubaddr = a - mx.Unlock() + result.Lock() + result.public++ + result.pubaddr = a + result.Unlock() case IsDialError(err): log.Debugf("Dialback through %s failed", pi.ID.Pretty()) - mx.Lock() - private++ - mx.Unlock() + result.Lock() + result.private++ + result.Unlock() default: log.Debugf("Dialback error through %s: %s", pi.ID.Pretty(), err) @@ -184,7 +186,7 @@ func (as *AmbientAutoNAT) autodetect() { wg.Wait() as.mx.Lock() - if public > 0 { + if result.public > 0 { log.Debugf("NAT status is public") if as.status == NATStatusPrivate { as.confidence = 0 @@ -192,8 +194,8 @@ func (as *AmbientAutoNAT) autodetect() { as.confidence++ } as.status = NATStatusPublic - as.addr = pubaddr - } else if private > 0 { + as.addr = result.pubaddr + } else if result.private > 0 { log.Debugf("NAT status is private") if as.status == NATStatusPublic { as.confidence = 0 From 8244eed07a5413e371de1e9c5083e7be9e2caa34 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 2 Apr 2019 20:17:18 +0300 Subject: [PATCH 4/6] add comments for status flips dropping confidence to 0 --- autonat.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autonat.go b/autonat.go index 46d949d..fcb110a 100644 --- a/autonat.go +++ b/autonat.go @@ -189,6 +189,7 @@ func (as *AmbientAutoNAT) autodetect() { if result.public > 0 { log.Debugf("NAT status is public") if as.status == NATStatusPrivate { + // we are flipping our NATStatus, so confidence drops to 0 as.confidence = 0 } else if as.confidence < 3 { as.confidence++ @@ -198,6 +199,7 @@ func (as *AmbientAutoNAT) autodetect() { } else if result.private > 0 { log.Debugf("NAT status is private") if as.status == NATStatusPublic { + // we are flipping our NATStatus, so confidence drops to 0 as.confidence = 0 } else if as.confidence < 3 { as.confidence++ From f645f1167f9761c13b75656f1e75bae30ec3fb1f Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 2 Apr 2019 20:20:10 +0300 Subject: [PATCH 5/6] don't flip to status unknown on first faiulure, use the confidence level --- autonat.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autonat.go b/autonat.go index fcb110a..10fbc8f 100644 --- a/autonat.go +++ b/autonat.go @@ -206,11 +206,13 @@ func (as *AmbientAutoNAT) autodetect() { } as.status = NATStatusPrivate as.addr = nil + } else if as.confidence > 0 { + // don't just flip to unknown, reduce confidence first + as.confidence-- } else { log.Debugf("NAT status is unknown") as.status = NATStatusUnknown as.addr = nil - as.confidence = 0 } as.mx.Unlock() } From 2584eaf3ee4c9bfa477c5ce1929dd8236274549d Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 3 Apr 2019 22:11:03 +0300 Subject: [PATCH 6/6] defer wg.Done --- autonat.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autonat.go b/autonat.go index 10fbc8f..60188c9 100644 --- a/autonat.go +++ b/autonat.go @@ -158,6 +158,8 @@ func (as *AmbientAutoNAT) autodetect() { for _, pi := range peers[:probe] { wg.Add(1) go func(pi pstore.PeerInfo) { + defer wg.Done() + as.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL) a, err := cli.DialBack(ctx, pi.ID) @@ -178,8 +180,6 @@ func (as *AmbientAutoNAT) autodetect() { default: log.Debugf("Dialback error through %s: %s", pi.ID.Pretty(), err) } - - wg.Done() }(pi) }