diff options
author | holisticode <holistic.computing@gmail.com> | 2019-02-15 02:01:50 +0800 |
---|---|---|
committer | Viktor TrĂ³n <viktor.tron@gmail.com> | 2019-02-15 02:01:50 +0800 |
commit | 2af24724dd5f3ab1994001854eb32c6a19f9f64a (patch) | |
tree | 3b683db9b4ceccf95d6d7bb641f3c13dda4bf483 /swarm/network/kademlia.go | |
parent | fab8c5a1cd6a1ef54922abe33db8bfaa1c73f169 (diff) | |
download | go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.tar go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.tar.gz go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.tar.bz2 go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.tar.lz go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.tar.xz go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.tar.zst go-tangerine-2af24724dd5f3ab1994001854eb32c6a19f9f64a.zip |
swarm/network: Saturation check for healthy networks (#19071)
* swarm/network: new saturation for implementation
* swarm/network: re-added saturation func in Kademlia as it is used elsewhere
* swarm/network: saturation with higher MinBinSize
* swarm/network: PeersPerBin with depth check
* swarm/network: edited tests to pass new saturated check
* swarm/network: minor fix saturated check
* swarm/network/simulations/discovery: fixed renamed RPC call
* swarm/network: renamed to isSaturated and returns bool
* swarm/network: early depth check
Diffstat (limited to 'swarm/network/kademlia.go')
-rw-r--r-- | swarm/network/kademlia.go | 74 |
1 files changed, 64 insertions, 10 deletions
diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index 1193e3b65..146f39106 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -628,7 +628,8 @@ func (k *Kademlia) string() string { // used for testing only // TODO move to separate testing tools file type PeerPot struct { - NNSet [][]byte + NNSet [][]byte + PeersPerBin []int } // NewPeerPotMap creates a map of pot record of *BzzAddr with keys @@ -654,6 +655,7 @@ func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot { // all nn-peers var nns [][]byte + peersPerBin := make([]int, depth) // iterate through the neighbours, going from the deepest to the shallowest np.EachNeighbour(a, Pof, func(val pot.Val, po int) bool { @@ -667,14 +669,18 @@ func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot { // a neighbor is any peer in or deeper than the depth if po >= depth { nns = append(nns, addr) - return true + } else { + // for peers < depth, we just count the number in each bin + // the bin is the index of the slice + peersPerBin[po]++ } - return false + return true }) - log.Trace(fmt.Sprintf("%x PeerPotMap NNS: %s", addrs[i][:4], LogAddrs(nns))) + log.Trace(fmt.Sprintf("%x PeerPotMap NNS: %s, peersPerBin", addrs[i][:4], LogAddrs(nns))) ppmap[common.Bytes2Hex(a)] = &PeerPot{ - NNSet: nns, + NNSet: nns, + PeersPerBin: peersPerBin, } } return ppmap @@ -698,6 +704,39 @@ func (k *Kademlia) saturation() int { return prev } +// isSaturated returns true if the kademlia is considered saturated, or false if not. +// It checks this by checking an array of ints called unsaturatedBins; each item in that array corresponds +// to the bin which is unsaturated (number of connections < k.MinBinSize). +// The bin is considered unsaturated only if there are actual peers in that PeerPot's bin (peersPerBin) +// (if there is no peer for a given bin, then no connection could ever be established; +// in a God's view this is relevant as no more peers will ever appear on that bin) +func (k *Kademlia) isSaturated(peersPerBin []int, depth int) bool { + // depth could be calculated from k but as this is called from `GetHealthInfo()`, + // the depth has already been calculated so we can require it as a parameter + + // early check for depth + if depth != len(peersPerBin) { + return false + } + unsaturatedBins := make([]int, 0) + k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { + + if po >= depth { + return false + } + log.Trace("peers per bin", "peersPerBin[po]", peersPerBin[po], "po", po) + // if there are actually peers in the PeerPot who can fulfill k.MinBinSize + if size < k.MinBinSize && size < peersPerBin[po] { + log.Trace("connections for po", "po", po, "size", size) + unsaturatedBins = append(unsaturatedBins, po) + } + return true + }) + + log.Trace("list of unsaturated bins", "unsaturatedBins", unsaturatedBins) + return len(unsaturatedBins) == 0 +} + // knowNeighbours tests if all neighbours in the peerpot // are found among the peers known to the kademlia // It is used in Healthy function for testing only @@ -780,11 +819,13 @@ type Health struct { ConnectNN bool // whether node is connected to all its neighbours CountConnectNN int // amount of neighbours connected to MissingConnectNN [][]byte // which neighbours we should have been connected to but we're not - Saturated bool // whether we are connected to all the peers we would have liked to - Hive string + // Saturated: if in all bins < depth number of connections >= MinBinsize or, + // if number of connections < MinBinSize, to the number of available peers in that bin + Saturated bool + Hive string } -// Healthy reports the health state of the kademlia connectivity +// GetHealthInfo reports the health state of the kademlia connectivity // // The PeerPot argument provides an all-knowing view of the network // The resulting Health object is a result of comparisons between @@ -792,7 +833,7 @@ type Health struct { // what SHOULD it have been when we take all we know about the network into consideration. // // used for testing only -func (k *Kademlia) Healthy(pp *PeerPot) *Health { +func (k *Kademlia) GetHealthInfo(pp *PeerPot) *Health { k.lock.RLock() defer k.lock.RUnlock() if len(pp.NNSet) < k.NeighbourhoodSize { @@ -801,7 +842,10 @@ func (k *Kademlia) Healthy(pp *PeerPot) *Health { gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet) knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet) depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) - saturated := k.saturation() < depth + + // check saturation + saturated := k.isSaturated(pp.PeersPerBin, depth) + log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated)) return &Health{ KnowNN: knownn, @@ -814,3 +858,13 @@ func (k *Kademlia) Healthy(pp *PeerPot) *Health { Hive: k.string(), } } + +// Healthy return the strict interpretation of `Healthy` given a `Health` struct +// definition of strict health: all conditions must be true: +// - we at least know one peer +// - we know all neighbors +// - we are connected to all known neighbors +// - it is saturated +func (h *Health) Healthy() bool { + return h.KnowNN && h.ConnectNN && h.CountKnowNN > 0 && h.Saturated +} |