Skip to content

Commit 38cff0f

Browse files
committed
implement smart dialing
1 parent f7a45b6 commit 38cff0f

File tree

8 files changed

+1122
-87
lines changed

8 files changed

+1122
-87
lines changed

core/network/network.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,13 @@ type Dialer interface {
184184
Notify(Notifiee)
185185
StopNotify(Notifiee)
186186
}
187+
188+
// AddrDelay provides an address along with the delay after which the address
189+
// should be dialed
190+
type AddrDelay struct {
191+
Addr ma.Multiaddr
192+
Delay time.Duration
193+
}
194+
195+
// DialRanker provides a schedule of dialing the provided addresses
196+
type DialRanker func([]ma.Multiaddr) []AddrDelay

p2p/net/swarm/clock.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package swarm
2+
3+
import "time"
4+
5+
// InstantTimer is a timer that triggers at some instant rather than some duration
6+
type InstantTimer interface {
7+
Reset(d time.Time) bool
8+
Stop() bool
9+
Ch() <-chan time.Time
10+
}
11+
12+
// Clock is a clock that can create timers that trigger at some
13+
// instant rather than some duration
14+
type Clock interface {
15+
Now() time.Time
16+
Since(t time.Time) time.Duration
17+
InstantTimer(when time.Time) InstantTimer
18+
}
19+
20+
type RealTimer struct{ t *time.Timer }
21+
22+
var _ InstantTimer = (*RealTimer)(nil)
23+
24+
func (t RealTimer) Ch() <-chan time.Time {
25+
return t.t.C
26+
}
27+
28+
func (t RealTimer) Reset(d time.Time) bool {
29+
return t.t.Reset(time.Until(d))
30+
}
31+
32+
func (t RealTimer) Stop() bool {
33+
return t.t.Stop()
34+
}
35+
36+
type RealClock struct{}
37+
38+
var _ Clock = RealClock{}
39+
40+
func (RealClock) Now() time.Time {
41+
return time.Now()
42+
}
43+
func (RealClock) Since(t time.Time) time.Duration {
44+
return time.Since(t)
45+
}
46+
func (RealClock) InstantTimer(when time.Time) InstantTimer {
47+
t := time.NewTimer(time.Until(when))
48+
return &RealTimer{t}
49+
}

p2p/net/swarm/dial_ranker.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package swarm
2+
3+
import (
4+
"time"
5+
6+
"github.com/libp2p/go-libp2p/core/network"
7+
ma "github.com/multiformats/go-multiaddr"
8+
manet "github.com/multiformats/go-multiaddr/net"
9+
)
10+
11+
const (
12+
publicTCPDelay = 300 * time.Millisecond
13+
privateTCPDelay = 30 * time.Millisecond
14+
relayDelay = 500 * time.Millisecond
15+
)
16+
17+
func noDelayRanker(addrs []ma.Multiaddr) []network.AddrDelay {
18+
res := make([]network.AddrDelay, len(addrs))
19+
for i, a := range addrs {
20+
res[i] = network.AddrDelay{Addr: a, Delay: 0}
21+
}
22+
return res
23+
}
24+
25+
// defaultDialRanker is the default ranking logic.
26+
//
27+
// we consider private, public ip4, public ip6, relay addresses separately.
28+
//
29+
// In each group, if a quic address is present, we delay tcp addresses.
30+
//
31+
// private: 30 ms delay.
32+
// public ip4: 300 ms delay.
33+
// public ip6: 300 ms delay.
34+
//
35+
// If a quic-v1 address is present we don't dial quic or webtransport address on the same (ip,port) combination.
36+
// If a tcp address is present we don't dial ws or wss address on the same (ip, port) combination.
37+
// If direct addresses are present we delay all relay addresses by 500 millisecond
38+
func defaultDialRanker(addrs []ma.Multiaddr) []network.AddrDelay {
39+
ip4 := make([]ma.Multiaddr, 0, len(addrs))
40+
ip6 := make([]ma.Multiaddr, 0, len(addrs))
41+
pvt := make([]ma.Multiaddr, 0, len(addrs))
42+
relay := make([]ma.Multiaddr, 0, len(addrs))
43+
44+
res := make([]network.AddrDelay, 0, len(addrs))
45+
for _, a := range addrs {
46+
switch {
47+
case !manet.IsPublicAddr(a):
48+
pvt = append(pvt, a)
49+
case isRelayAddr(a):
50+
relay = append(relay, a)
51+
case isProtocolAddr(a, ma.P_IP4):
52+
ip4 = append(ip4, a)
53+
case isProtocolAddr(a, ma.P_IP6):
54+
ip6 = append(ip6, a)
55+
default:
56+
res = append(res, network.AddrDelay{Addr: a, Delay: 0})
57+
}
58+
}
59+
var roffset time.Duration = 0
60+
if len(ip4) > 0 || len(ip6) > 0 {
61+
roffset = relayDelay
62+
}
63+
64+
res = append(res, getAddrDelay(pvt, privateTCPDelay, 0)...)
65+
res = append(res, getAddrDelay(ip4, publicTCPDelay, 0)...)
66+
res = append(res, getAddrDelay(ip6, publicTCPDelay, 0)...)
67+
res = append(res, getAddrDelay(relay, publicTCPDelay, roffset)...)
68+
return res
69+
}
70+
71+
func getAddrDelay(addrs []ma.Multiaddr, tcpDelay time.Duration, offset time.Duration) []network.AddrDelay {
72+
var hasQuic, hasQuicV1 bool
73+
quicV1Addr := make(map[string]struct{})
74+
tcpAddr := make(map[string]struct{})
75+
for _, a := range addrs {
76+
switch {
77+
case isProtocolAddr(a, ma.P_WEBTRANSPORT):
78+
case isProtocolAddr(a, ma.P_QUIC):
79+
hasQuic = true
80+
case isProtocolAddr(a, ma.P_QUIC_V1):
81+
hasQuicV1 = true
82+
quicV1Addr[addrPort(a, ma.P_UDP)] = struct{}{}
83+
case isProtocolAddr(a, ma.P_WS) || isProtocolAddr(a, ma.P_WSS):
84+
case isProtocolAddr(a, ma.P_TCP):
85+
tcpAddr[addrPort(a, ma.P_TCP)] = struct{}{}
86+
}
87+
}
88+
89+
res := make([]network.AddrDelay, 0, len(addrs))
90+
for _, a := range addrs {
91+
delay := offset
92+
switch {
93+
case isProtocolAddr(a, ma.P_WEBTRANSPORT):
94+
if hasQuicV1 {
95+
if _, ok := quicV1Addr[addrPort(a, ma.P_UDP)]; ok {
96+
continue
97+
}
98+
}
99+
case isProtocolAddr(a, ma.P_QUIC):
100+
if hasQuicV1 {
101+
if _, ok := quicV1Addr[addrPort(a, ma.P_UDP)]; ok {
102+
continue
103+
}
104+
}
105+
case isProtocolAddr(a, ma.P_WS) || isProtocolAddr(a, ma.P_WSS):
106+
if _, ok := tcpAddr[addrPort(a, ma.P_TCP)]; ok {
107+
continue
108+
}
109+
if hasQuic || hasQuicV1 {
110+
delay += tcpDelay
111+
}
112+
case isProtocolAddr(a, ma.P_TCP):
113+
if hasQuic || hasQuicV1 {
114+
delay += tcpDelay
115+
}
116+
}
117+
res = append(res, network.AddrDelay{Addr: a, Delay: delay})
118+
}
119+
return res
120+
}
121+
122+
func addrPort(a ma.Multiaddr, p int) string {
123+
c, _ := ma.SplitFirst(a)
124+
port, _ := a.ValueForProtocol(p)
125+
return c.Value() + ":" + port
126+
}
127+
128+
func isProtocolAddr(a ma.Multiaddr, p int) bool {
129+
_, err := a.ValueForProtocol(p)
130+
return err == nil
131+
}

0 commit comments

Comments
 (0)