diff --git a/CHANGELOG.md b/CHANGELOG.md index a96ddf4d2e..60e9b2c45d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,11 @@ ## 🔦 Highlights ### Smart Dialing -* When connecting to a peer we now do dial prioritisation to prefer QUIC addresses over TCP addresses. We dial the QUIC addresses first and wait 250ms to dial the TCP addresses of the peer. -* In our experiments, we've seen little impact on latencies up to 95th percentile. Two new metrics have been added, dial ranking delay and dials per connection to gauge the impact. -* To avoid adversely impacting users in UDP black holed environments, this feature is disabled by default. -* To enable use `libp2p.EnableSmartDialing` option. +* When connecting to a peer we now do [happy eyeballs](https://www.rfc-editor.org/rfc/rfc8305) like dial prioritisation to prefer QUIC addresses over TCP addresses. We dial the QUIC address first and wait 250ms to dial the TCP address of the peer. +* In our experiments we've seen little impact on latencies up to 80th percentile. 90th and 95th percentile latencies are impacted. For details see discussion on the [PR](https://github.com/libp2p/go-libp2p/pull/2260#issuecomment-1528848170). * For details of the address ranking logic see godoc for `swarm.DefaultDialRanker`. - +* To disable smart dialing and keep the old behaviour use the +`libp2p.NoDelayNetworkDialRanker` option. # [v0.27.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.27.0) diff --git a/config/config.go b/config/config.go index 6b1c8181a0..e700bd1dc9 100644 --- a/config/config.go +++ b/config/config.go @@ -124,8 +124,8 @@ type Config struct { DisableMetrics bool PrometheusRegisterer prometheus.Registerer - SmartDialing bool - SmartDialingCustom bool + DialRanker network.DialRanker + DialRankerCustom bool } func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { @@ -176,9 +176,10 @@ func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swa if cfg.MultiaddrResolver != nil { opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver)) } - if !cfg.SmartDialing { - opts = append(opts, swarm.WithNoDialDelay()) + if cfg.DialRanker == nil { + cfg.DialRanker = swarm.NoDelayRanker } + opts = append(opts, swarm.WithDialRanker(cfg.DialRanker)) if enableMetrics { opts = append(opts, swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer)))) diff --git a/defaults.go b/defaults.go index 6c8ca0f49c..8428234cba 100644 --- a/defaults.go +++ b/defaults.go @@ -135,9 +135,9 @@ var DefaultPrometheusRegisterer = func(cfg *Config) error { return cfg.Apply(PrometheusRegisterer(prometheus.DefaultRegisterer)) } -// DefaultSmartDialing configures libp2p to disable smart dialing by default -var DefaultSmartDialing = func(cfg *Config) error { - return cfg.Apply(DisableSmartDialing()) +// DefaultDialRanker configures libp2p to disable smart dialing by default +var DefaultDialRanker = func(cfg *Config) error { + return cfg.Apply(DialRanker(nil)) } // Complete list of default options and when to fallback on them. @@ -197,8 +197,8 @@ var defaults = []struct { opt: DefaultPrometheusRegisterer, }, { - fallback: func(cfg *Config) bool { return !cfg.SmartDialingCustom }, - opt: DefaultSmartDialing, + fallback: func(cfg *Config) bool { return !cfg.DialRankerCustom }, + opt: DefaultDialRanker, }, } diff --git a/options.go b/options.go index 9315c19b90..6aa535bef3 100644 --- a/options.go +++ b/options.go @@ -575,31 +575,15 @@ func PrometheusRegisterer(reg prometheus.Registerer) Option { } } -// EnableSmartDialing configures libp2p to prioritise dials to certain -// addresses for outgoing connection attempts to a peer. This reduces the number -// of dial attempts with little impact to latency. see `swarm.DefaultDialRanker` -// for details of the ranking logic. -// (default: disabled) -func EnableSmartDialing() Option { - return func(cfg *Config) error { - if cfg.SmartDialingCustom { - return errors.New("smart dialing already configured") - } - cfg.SmartDialingCustom = true - cfg.SmartDialing = true - return nil - } -} - -// DisableSmartDialing configures libp2p to dial all addresses in parallel -// for outgoing connection attempts to a peer. -func DisableSmartDialing() Option { +// DialRanker configures libp2p to use d as the dial ranker. To enable smart +// dialing use `swarm.DefaultDialRanker`. nil disables smart dialing. +func DialRanker(d network.DialRanker) Option { return func(cfg *Config) error { - if cfg.SmartDialingCustom { - return errors.New("smart dialing already configured") + if cfg.DialRankerCustom { + return errors.New("dial ranker already configured") } - cfg.SmartDialingCustom = true - cfg.SmartDialing = false + cfg.DialRanker = d + cfg.DialRankerCustom = true return nil } } diff --git a/p2p/net/swarm/dial_ranker.go b/p2p/net/swarm/dial_ranker.go index a77e2cc6a5..0236995355 100644 --- a/p2p/net/swarm/dial_ranker.go +++ b/p2p/net/swarm/dial_ranker.go @@ -25,8 +25,8 @@ const ( RelayDelay = 250 * time.Millisecond ) -// noDelayRanker ranks addresses with no delay. This is useful for simultaneous connect requests. -func noDelayRanker(addrs []ma.Multiaddr) []network.AddrDelay { +// NoDelayRanker ranks addresses with no delay. This is useful for simultaneous connect requests. +func NoDelayRanker(addrs []ma.Multiaddr) []network.AddrDelay { return getAddrDelay(addrs, 0, 0, 0) } diff --git a/p2p/net/swarm/dial_ranker_test.go b/p2p/net/swarm/dial_ranker_test.go index 28cb6e8c1a..dc5030544a 100644 --- a/p2p/net/swarm/dial_ranker_test.go +++ b/p2p/net/swarm/dial_ranker_test.go @@ -47,7 +47,7 @@ func TestNoDelayRanker(t *testing.T) { } for _, tc := range testCase { t.Run(tc.name, func(t *testing.T) { - res := noDelayRanker(tc.addrs) + res := NoDelayRanker(tc.addrs) if len(res) != len(tc.output) { log.Errorf("expected %s got %s", tc.output, res) t.Errorf("expected elems: %d got: %d", len(tc.output), len(res)) diff --git a/p2p/net/swarm/dial_worker.go b/p2p/net/swarm/dial_worker.go index 8d574eba7f..163b1a96f2 100644 --- a/p2p/net/swarm/dial_worker.go +++ b/p2p/net/swarm/dial_worker.go @@ -415,7 +415,7 @@ func (w *dialWorker) dispatchError(ad *addrDial, err error) { // dial all addresses immediately without any delay func (w *dialWorker) rankAddrs(addrs []ma.Multiaddr, isSimConnect bool) []network.AddrDelay { if isSimConnect { - return noDelayRanker(addrs) + return NoDelayRanker(addrs) } return w.s.dialRanker(addrs) } diff --git a/p2p/net/swarm/dial_worker_test.go b/p2p/net/swarm/dial_worker_test.go index 903a9e500c..f05609417b 100644 --- a/p2p/net/swarm/dial_worker_test.go +++ b/p2p/net/swarm/dial_worker_test.go @@ -787,7 +787,7 @@ func TestCheckDialWorkerLoopScheduling(t *testing.T) { s4 := makeSwarmWithNoListenAddrs(t) defer s4.Close() // invalid ranking logic to trigger an error - s3.dialRanker = noDelayRanker + s3.dialRanker = NoDelayRanker err = checkDialWorkerLoopScheduling(t, s3, s4, tc) require.Error(t, err) } diff --git a/p2p/net/swarm/swarm.go b/p2p/net/swarm/swarm.go index eaae4bcfc8..0bffab99a2 100644 --- a/p2p/net/swarm/swarm.go +++ b/p2p/net/swarm/swarm.go @@ -100,15 +100,6 @@ func WithResourceManager(m network.ResourceManager) Option { } } -// WithNoDialDelay configures swarm to dial all addresses for a peer without -// any delay -func WithNoDialDelay() Option { - return func(s *Swarm) error { - s.dialRanker = noDelayRanker - return nil - } -} - // WithDialRanker configures swarm to use d as the DialRanker func WithDialRanker(d network.DialRanker) Option { return func(s *Swarm) error {