mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
swarm/network: new saturation for implementation
This commit is contained in:
parent
7c60d0a6a2
commit
6f81df32bb
2 changed files with 125 additions and 33 deletions
|
|
@ -626,6 +626,7 @@ func (k *Kademlia) string() string {
|
||||||
// TODO move to separate testing tools file
|
// TODO move to separate testing tools file
|
||||||
type PeerPot struct {
|
type PeerPot struct {
|
||||||
NNSet [][]byte
|
NNSet [][]byte
|
||||||
|
PeersPerBin []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPeerPotMap creates a map of pot record of *BzzAddr with keys
|
// NewPeerPotMap creates a map of pot record of *BzzAddr with keys
|
||||||
|
|
@ -651,6 +652,7 @@ func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot {
|
||||||
|
|
||||||
// all nn-peers
|
// all nn-peers
|
||||||
var nns [][]byte
|
var nns [][]byte
|
||||||
|
peersPerBin := make([]int, depth)
|
||||||
|
|
||||||
// iterate through the neighbours, going from the deepest to the shallowest
|
// iterate through the neighbours, going from the deepest to the shallowest
|
||||||
np.EachNeighbour(a, Pof, func(val pot.Val, po int) bool {
|
np.EachNeighbour(a, Pof, func(val pot.Val, po int) bool {
|
||||||
|
|
@ -664,35 +666,54 @@ func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot {
|
||||||
// a neighbor is any peer in or deeper than the depth
|
// a neighbor is any peer in or deeper than the depth
|
||||||
if po >= depth {
|
if po >= depth {
|
||||||
nns = append(nns, addr)
|
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{
|
ppmap[common.Bytes2Hex(a)] = &PeerPot{
|
||||||
NNSet: nns,
|
NNSet: nns,
|
||||||
|
PeersPerBin: peersPerBin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ppmap
|
return ppmap
|
||||||
}
|
}
|
||||||
|
|
||||||
// saturation returns the smallest po value in which the node has less than MinBinSize peers
|
// getUnsaturatedBins returns an array of ints; each item in that array corresponds
|
||||||
// if the iterator reaches neighbourhood radius, then the last bin + 1 is returned
|
// to the bin which is unsaturated (number of connections < k.MinBinSize).
|
||||||
func (k *Kademlia) saturation() int {
|
// The bin is considered unsaturated only if there are actual peers in that PeerPot's bin (peersPerBin)
|
||||||
prev := -1
|
// (if there is no peer for a given bin, then no connection could ever be established;
|
||||||
radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base)
|
// in a God's view this is relevant as no more peers will ever appear on that bin)
|
||||||
|
func (k *Kademlia) getUnsaturatedBins(peersPerBin []int, depth int) []int {
|
||||||
|
// depth could be calculated from k but as this is called from `Healthy()`,
|
||||||
|
// the depth has already been calculated so we can require it as a parameter
|
||||||
|
connectedPeersPerBin := make([]int, depth)
|
||||||
k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool {
|
k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool {
|
||||||
prev++
|
|
||||||
if po >= radius {
|
if po >= depth {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return prev == po && size >= k.MinBinSize
|
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 prev < 0 {
|
if peersPerBin[po] >= k.MinBinSize {
|
||||||
return 0
|
log.Trace("connections for po", "po", po, "size", size)
|
||||||
|
connectedPeersPerBin[po] += size
|
||||||
}
|
}
|
||||||
return prev
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
unsaturatedBins := make([]int, 0)
|
||||||
|
for i := 0; i < len(connectedPeersPerBin); i++ {
|
||||||
|
if connectedPeersPerBin[i] > 0 && connectedPeersPerBin[i] < k.MinBinSize {
|
||||||
|
unsaturatedBins = append(unsaturatedBins, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Trace("list of unsaturated bins", "unsaturatedBins", unsaturatedBins)
|
||||||
|
return unsaturatedBins
|
||||||
}
|
}
|
||||||
|
|
||||||
// knowNeighbours tests if all neighbours in the peerpot
|
// knowNeighbours tests if all neighbours in the peerpot
|
||||||
|
|
@ -777,7 +798,9 @@ type Health struct {
|
||||||
ConnectNN bool // whether node is connected to all its neighbours
|
ConnectNN bool // whether node is connected to all its neighbours
|
||||||
CountConnectNN int // amount of neighbours connected to
|
CountConnectNN int // amount of neighbours connected to
|
||||||
MissingConnectNN [][]byte // which neighbours we should have been connected to but we're not
|
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
|
// 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
|
Hive string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -798,7 +821,10 @@ func (k *Kademlia) Healthy(pp *PeerPot) *Health {
|
||||||
gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet)
|
gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet)
|
||||||
knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet)
|
knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet)
|
||||||
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
||||||
saturated := k.saturation() < depth
|
// check saturation
|
||||||
|
unsaturatedBins := k.getUnsaturatedBins(pp.PeersPerBin, depth)
|
||||||
|
saturated := len(unsaturatedBins) == 0
|
||||||
|
|
||||||
log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated))
|
log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated))
|
||||||
return &Health{
|
return &Health{
|
||||||
KnowNN: knownn,
|
KnowNN: knownn,
|
||||||
|
|
@ -811,3 +837,13 @@ func (k *Kademlia) Healthy(pp *PeerPot) *Health {
|
||||||
Hive: k.string(),
|
Hive: k.string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsHealthyStrict 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) IsHealthyStrict() bool {
|
||||||
|
return h.KnowNN && h.ConnectNN && h.CountKnowNN > 0 && h.Saturated
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,60 +176,116 @@ func TestHealthStrict(t *testing.T) {
|
||||||
// no peers
|
// no peers
|
||||||
// unhealthy (and lonely)
|
// unhealthy (and lonely)
|
||||||
tk := newTestKademlia(t, "11111111")
|
tk := newTestKademlia(t, "11111111")
|
||||||
tk.checkHealth(false, false)
|
tk.checkHealth(false)
|
||||||
|
|
||||||
// know one peer but not connected
|
// know one peer but not connected
|
||||||
// unhealthy
|
// unhealthy
|
||||||
tk.Register("11100000")
|
tk.Register("11100000")
|
||||||
tk.checkHealth(false, false)
|
tk.checkHealth(false)
|
||||||
|
|
||||||
// know one peer and connected
|
// know one peer and connected
|
||||||
// healthy
|
// unhealthy: not saturated
|
||||||
tk.On("11100000")
|
tk.On("11100000")
|
||||||
tk.checkHealth(true, false)
|
tk.checkHealth(true)
|
||||||
|
|
||||||
// know two peers, only one connected
|
// know two peers, only one connected
|
||||||
// unhealthy
|
// unhealthy
|
||||||
tk.Register("11111100")
|
tk.Register("11111100")
|
||||||
tk.checkHealth(false, false)
|
tk.checkHealth(false)
|
||||||
|
|
||||||
// know two peers and connected to both
|
// know two peers and connected to both
|
||||||
// healthy
|
// healthy
|
||||||
tk.On("11111100")
|
tk.On("11111100")
|
||||||
tk.checkHealth(true, false)
|
tk.checkHealth(true)
|
||||||
|
|
||||||
// know three peers, connected to the two deepest
|
// know three peers, connected to the two deepest
|
||||||
// healthy
|
// healthy
|
||||||
tk.Register("00000000")
|
tk.Register("00000000")
|
||||||
tk.checkHealth(true, false)
|
tk.checkHealth(true)
|
||||||
|
|
||||||
// know three peers, connected to all three
|
// know three peers, connected to all three
|
||||||
// healthy
|
// healthy
|
||||||
tk.On("00000000")
|
tk.On("00000000")
|
||||||
tk.checkHealth(true, false)
|
tk.checkHealth(true)
|
||||||
|
|
||||||
// add fourth peer deeper than current depth
|
// add fourth peer deeper than current depth
|
||||||
// unhealthy
|
// unhealthy
|
||||||
tk.Register("11110000")
|
tk.Register("11110000")
|
||||||
tk.checkHealth(false, false)
|
tk.checkHealth(false)
|
||||||
|
|
||||||
// connected to three deepest peers
|
// connected to three deepest peers
|
||||||
// healthy
|
// healthy
|
||||||
tk.On("11110000")
|
tk.On("11110000")
|
||||||
tk.checkHealth(true, false)
|
tk.checkHealth(true)
|
||||||
|
|
||||||
// add additional peer in same bin as deepest peer
|
// add additional peer in same bin as deepest peer
|
||||||
// unhealthy
|
// unhealthy
|
||||||
tk.Register("11111101")
|
tk.Register("11111101")
|
||||||
tk.checkHealth(false, false)
|
tk.checkHealth(false)
|
||||||
|
|
||||||
// four deepest of five peers connected
|
// four deepest of five peers connected
|
||||||
// healthy
|
// healthy
|
||||||
tk.On("11111101")
|
tk.On("11111101")
|
||||||
tk.checkHealth(true, false)
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// add additional peer in bin 0
|
||||||
|
// unhealthy: unsaturated bin 0, 2 known but 1 connected
|
||||||
|
tk.Register("00000001")
|
||||||
|
tk.checkHealth(false)
|
||||||
|
|
||||||
|
// Connect second in bin 0
|
||||||
|
// healthy
|
||||||
|
tk.On("00000001")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// add peer in bin 1
|
||||||
|
// healthy, as it is known but not connected
|
||||||
|
tk.Register("10000000")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// connect peer in bin 1
|
||||||
|
// depth change, is now 1
|
||||||
|
// healthy, 1 peer in bin 1 known and connected
|
||||||
|
tk.On("10000000")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// add second peer in bin 1
|
||||||
|
// unhealthy, as it is known but not connected
|
||||||
|
tk.Register("10000001")
|
||||||
|
tk.checkHealth(false)
|
||||||
|
|
||||||
|
// connect second peer in bin 1
|
||||||
|
// healthy,
|
||||||
|
tk.On("10000001")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// connect third peer in bin 1
|
||||||
|
// healthy,
|
||||||
|
tk.On("10000011")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// add peer in bin 2
|
||||||
|
// healthy, no depth change
|
||||||
|
tk.Register("11000000")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// connect peer in bin 2
|
||||||
|
// depth change - as we already have peers in bin 3 and 4,
|
||||||
|
// we have contiguous bins, no bin < po 5 is empty -> depth 5
|
||||||
|
// healthy, every bin < depth has the max available peers,
|
||||||
|
// even if they are < MinBinSize
|
||||||
|
tk.On("11000000")
|
||||||
|
tk.checkHealth(true)
|
||||||
|
|
||||||
|
// add peer in bin 2
|
||||||
|
// unhealthy, peer bin is below depth 5 but
|
||||||
|
// has more available peers (2) than connected ones (1)
|
||||||
|
// --> unsaturated
|
||||||
|
tk.Register("11000011")
|
||||||
|
tk.checkHealth(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tk *testKademlia) checkHealth(expectHealthy bool, expectSaturation bool) {
|
func (tk *testKademlia) checkHealth(expectHealthy bool) {
|
||||||
tk.t.Helper()
|
tk.t.Helper()
|
||||||
kid := common.Bytes2Hex(tk.BaseAddr())
|
kid := common.Bytes2Hex(tk.BaseAddr())
|
||||||
addrs := [][]byte{tk.BaseAddr()}
|
addrs := [][]byte{tk.BaseAddr()}
|
||||||
|
|
@ -245,7 +301,7 @@ func (tk *testKademlia) checkHealth(expectHealthy bool, expectSaturation bool) {
|
||||||
// - we at least know one peer
|
// - we at least know one peer
|
||||||
// - we know all neighbors
|
// - we know all neighbors
|
||||||
// - we are connected to all known neighbors
|
// - we are connected to all known neighbors
|
||||||
health := healthParams.KnowNN && healthParams.ConnectNN && healthParams.CountKnowNN > 0
|
health := healthParams.IsHealthyStrict()
|
||||||
if expectHealthy != health {
|
if expectHealthy != health {
|
||||||
tk.t.Fatalf("expected kademlia health %v, is %v\n%v", expectHealthy, health, tk.String())
|
tk.t.Fatalf("expected kademlia health %v, is %v\n%v", expectHealthy, health, tk.String())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue