metrics.go 4.41 KB
Newer Older
Adin Schmahmann's avatar
Adin Schmahmann committed
1 2 3
package kpeerset

import (
4
	"math/big"
Adin Schmahmann's avatar
Adin Schmahmann committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
	"sort"
	"time"

	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/peerstore"
)

type peerLatencyMetric struct {
	peerMetric
	connectedness network.Connectedness
	latency       time.Duration
}

type peerLatencyMetricList []peerLatencyMetric

func (p peerLatencyMetricList) Len() int { return len(p) }
func (p peerLatencyMetricList) Less(i, j int) bool {
	pm1, pm2 := p[i], p[j]
24 25 26 27
	return calculationLess(pm1, pm2)
}
func (p peerLatencyMetricList) Swap(i, j int)           { p[i], p[j] = p[j], p[i] }
func (p peerLatencyMetricList) GetPeerID(i int) peer.ID { return p[i].peer }
Adin Schmahmann's avatar
Adin Schmahmann committed
28

29
func less(pm1, pm2 *peerLatencyMetric) bool {
Adin Schmahmann's avatar
Adin Schmahmann committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	p1Connectedness, p2Connectedness := pm1.connectedness, pm2.connectedness
	p1Latency, p2Latency := pm1.latency, pm2.latency

	// Compare latency assuming that connected is lower latency than unconnected
	if p1Connectedness == network.Connected {
		if p2Connectedness == network.Connected {
			return p1Latency < p2Latency
		}
		return true
	}
	if p2Connectedness == network.Connected {
		return false
	}

	// Compare latency assuming recent connection is lower latency than older connection.
	// TODO: This assumption largely stems from our latency library showing peers we know nothing about as
	// having zero latency
	if p1Connectedness == network.CanConnect {
		if p2Connectedness == network.CanConnect {
			return p1Latency > p2Latency
		}
		return true
	}
	if p2Connectedness == network.CanConnect {
		return false
	}

	// Check if either peer has proven to be unconnectable, if so rank them low
	if p1Connectedness == network.CannotConnect && p2Connectedness != network.CannotConnect {
		return false
	}
	if p2Connectedness == network.CannotConnect && p1Connectedness != network.CannotConnect {
		return true
	}

	return pm1.metric.Cmp(pm2.metric) == -1
}
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

func calculationLess(pm1, pm2 peerLatencyMetric) bool {
	return calc(pm1).Cmp(calc(pm2)) == -1
}

func calc(pm peerLatencyMetric) *big.Int {
	var c int64
	switch pm.connectedness {
	case network.Connected:
		c = 1
	case network.CanConnect:
		c = 5
	case network.CannotConnect:
		c = 10000
	default:
		c = 20
	}

	l := int64(pm.latency)
	if l <= 0 {
		l = int64(time.Second) * 10
	}

	res := big.NewInt(c)
	tmp := big.NewInt(l)
	res.Mul(res, tmp)
	res.Mul(res, pm.metric)

	return res
}
Adin Schmahmann's avatar
Adin Schmahmann committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

var _ SortablePeers = (*peerLatencyMetricList)(nil)

func PeersSortedByLatency(peers []IPeerMetric, net network.Network, metrics peerstore.Metrics) SortablePeers {
	lst := make(peerLatencyMetricList, len(peers))
	for i := range lst {
		p := peers[i].Peer()
		lst[i] = peerLatencyMetric{
			peerMetric:    peerMetric{peer: p, metric: peers[i].Metric()},
			connectedness: net.Connectedness(p),
			latency:       metrics.LatencyEWMA(p),
		}
	}
	sort.Sort(lst)
	return lst
}

func SortByLatency(net network.Network, metrics peerstore.Metrics) func(peers []*peerMetric) []peer.ID {
	return func(peers []*peerMetric) []peer.ID {
		metricLst := NewPeerMetricList(peers, func(p1, p2 *peerMetric) bool {
			p1Connectedness := net.Connectedness(p1.peer)
			p2Connectedness := net.Connectedness(p2.peer)

			// Compare latency assuming that connected is lower latency than unconnected
			if p1Connectedness == network.Connected {
				if p2Connectedness == network.Connected {
					return metrics.LatencyEWMA(p1.peer) > metrics.LatencyEWMA(p2.peer)
				}
				return true
			}
			if p2Connectedness == network.Connected {
				return false
			}

			// Compare latency assuming recent connection is lower latency than older connection.
			// TODO: This assumption largely stems from our latency library showing peers we know nothing about as
			// having zero latency
			if p1Connectedness == network.CanConnect {
				if p2Connectedness == network.CanConnect {
					return metrics.LatencyEWMA(p1.peer) > metrics.LatencyEWMA(p2.peer)
				}
				return true
			}
			if p2Connectedness == network.CanConnect {
				return false
			}

			// Check if either peer has proven to be unconnectable, if so rank them low
			if p1Connectedness == network.CannotConnect && p2Connectedness != network.CannotConnect {
				return false
			}
			if p2Connectedness == network.CannotConnect && p1Connectedness != network.CannotConnect {
				return true
			}

			return p1.metric.Cmp(p2.metric) == -1
		})

		sort.Stable(metricLst)
		peerLst := make([]peer.ID, metricLst.Len())
		for i := range peerLst {
			peerLst[i] = metricLst.GetPeerID(i)
		}

		return peerLst
	}
}