Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swarm: improve documentation for the DefaultDialRanker #2336

Merged
merged 1 commit into from
Jun 6, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions p2p/net/swarm/dial_ranker.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,38 +30,46 @@ func noDelayRanker(addrs []ma.Multiaddr) []network.AddrDelay {
return getAddrDelay(addrs, 0, 0, 0)
}

// DefaultDialRanker is the default ranking logic.
// DefaultDialRanker determines the ranking of outgoing connection attempts.
//
// We rank private, public ip4, public ip6, relay addresses separately.
// We do not prefer IPv6 over IPv4 as recommended by Happy Eyeballs RFC 8305. Currently there is no
// mechanism to detect an IPv6 blackhole, so we dial both IPv4 and IPv6 addresses in parallel.
// If direct addresses are present we delay all relay addresses by 500 millisecond

// In each group we apply the following logic:
// Addresses are grouped into four distinct groups:
//
// - private addresses (localhost and local networks (RFC 1918))
// - public IPv4 addresses
// - public IPv6 addresses
// - relay addresses
//
// First we filter the addresses we don't want to dial. We are filtering these addresses because we
// have an address that we prefer more than that address and which has the same reachability
// Within each group, the addresses are ranked according to the ranking logic described below.
// We then dial addresses according to this ranking, with short timeouts applied between dial attempts.
// This ranking logic dramatically reduces the number of simultaneous dial attempts, while introducing
// no additional latency in the vast majority of cases.
//
// If a QUIC-v1 address is present we don't dial QUIC or webtransport address on the same (ip,port)
// combination. If a QUICDraft29 or webtransport address is reachable, QUIC-v1 will definitely be
// reachable. QUICDraft29 is deprecated in favour of QUIC-v1 and QUIC-v1 is more performant than
// webtransport
// The private, public IPv4 and public IPv6 groups are dialed in parallel.
// Dialing relay addresses is delayed by 500 ms, if we have any non-relay alternatives.
//
// If a TCP address is present we don't dial ws or wss address on the same (ip, port) combination.
// If a ws address is reachable, TCP will definitely be reachable and it'll be more performant
// In a future iteration, IPv6 will be given a headstart over IPv4, as recommended by Happy Eyeballs RFC 8305.
// This is not enabled yet, since some ISPs are still IPv4-only, and dialing IPv6 addresses will therefore
// always fail.
// The correct solution is to detect this situation, and not attempt to dial IPv6 addresses at all.
// IPv6 blackhole detection is tracked in https://github.com/libp2p/go-libp2p/issues/1605.
//
// Then we rank the addresses:
// Within each group (private, public IPv4, public IPv6, relay addresses) we apply the following logic:
//
// If two QUIC addresses are present, we dial the QUIC address with the lowest port first. This is more
// likely to be the listen port. After this we dial the rest of the QUIC addresses delayed by QUICDelay.
// 1. Filter out addresses we don't want to dial:
// 1. If a /quic-v1 address is present, filter out /quic and /webtransport address on the same 2-tuple:
// QUIC v1 is preferred over the deprecated QUIC draft-29, and given the choice, we prefer using
// raw QUIC over using WebTransport.
// 2. If a /tcp address is present, filter out /ws or /wss addresses on the same 2-tuple:
// We prefer using raw TCP over using WebSocket.
//
// If a QUIC or webtransport address is present, TCP address dials are delayed by TCPDelay relative to
// the last QUIC dial.
// 2. Rank addresses:
//
// TCPDelay for public ip4 and public ip6 is PublicTCPDelay
// TCPDelay for private addresses is PrivateTCPDelay
// QUICDelay for public addresses is PublicQUICDelay
// QUICDelay for private addresses is PrivateQUICDelay
// 1. If two QUIC addresses are present, dial the QUIC address with the lowest port first:
// This is more likely to be the listen port. After this we dial the rest of the QUIC addresses delayed by
// 250ms (PublicQUICDelay) for public addresses, and 30ms (PrivateQUICDelay) for local addresses.
// 2. If a QUIC or WebTransport address is present, TCP addresses dials are delayed relative to the last QUIC dial:
// We prefer to end up with a QUIC connection. For public addresses, the delay introduced is 250ms (PublicTCPDelay),
// and for private addresses 30ms (PrivateTCPDelay).
func DefaultDialRanker(addrs []ma.Multiaddr) []network.AddrDelay {
relay, addrs := filterAddrs(addrs, isRelayAddr)
pvt, addrs := filterAddrs(addrs, manet.IsPrivateAddr)
Expand Down