Commit 78aba90f authored by acud's avatar acud Committed by GitHub

kademlia: add unsaturated bins to depth calculation (#1553)

parent 63f2ef34
...@@ -555,6 +555,32 @@ func recalcDepth(peers *pslice.PSlice) uint8 { ...@@ -555,6 +555,32 @@ func recalcDepth(peers *pslice.PSlice) uint8 {
shallowestEmpty, noEmptyBins = peers.ShallowestEmpty() shallowestEmpty, noEmptyBins = peers.ShallowestEmpty()
) )
shallowestUnsaturated := uint8(0)
binCount := 0
_ = peers.EachBinRev(func(_ swarm.Address, bin uint8) (bool, bool, error) {
if bin == shallowestUnsaturated {
binCount++
return false, false, nil
}
if bin > shallowestUnsaturated && binCount < saturationPeers {
// this means we have less than saturationPeers in the previous bin
// therefore we can return assuming that bin is the unsaturated one.
return true, false, nil
}
// bin > shallowestUnsaturated && binCount >= saturationPeers
shallowestUnsaturated = bin
binCount = 1
return false, false, nil
})
// if there are some empty bins and the shallowestEmpty is
// smaller than the shallowestUnsaturated then set shallowest
// unsaturated to the empty bin.
if !noEmptyBins && shallowestEmpty < shallowestUnsaturated {
shallowestUnsaturated = shallowestEmpty
}
_ = peers.EachBin(func(_ swarm.Address, po uint8) (bool, bool, error) { _ = peers.EachBin(func(_ swarm.Address, po uint8) (bool, bool, error) {
peersCtr++ peersCtr++
if peersCtr >= nnLowWatermark { if peersCtr >= nnLowWatermark {
...@@ -563,12 +589,11 @@ func recalcDepth(peers *pslice.PSlice) uint8 { ...@@ -563,12 +589,11 @@ func recalcDepth(peers *pslice.PSlice) uint8 {
} }
return false, false, nil return false, false, nil
}) })
if shallowestUnsaturated > candidate {
if noEmptyBins || shallowestEmpty > candidate {
return candidate return candidate
} }
return shallowestEmpty return shallowestUnsaturated
} }
// connect connects to a peer and gossips its address to our connected peers, // connect connects to a peer and gossips its address to our connected peers,
...@@ -889,8 +914,8 @@ func (k *Kad) ClosestPeer(addr swarm.Address, skipPeers ...swarm.Address) (swarm ...@@ -889,8 +914,8 @@ func (k *Kad) ClosestPeer(addr swarm.Address, skipPeers ...swarm.Address) (swarm
} }
// IsWithinDepth returns if an address is within the neighborhood depth of a node. // IsWithinDepth returns if an address is within the neighborhood depth of a node.
func (k *Kad) IsWithinDepth(adr swarm.Address) bool { func (k *Kad) IsWithinDepth(addr swarm.Address) bool {
return swarm.Proximity(k.base.Bytes(), adr.Bytes()) >= k.NeighborhoodDepth() return swarm.Proximity(k.base.Bytes(), addr.Bytes()) >= k.NeighborhoodDepth()
} }
// // EachNeighbor iterates from closest bin to farthest of the neighborhood peers. // // EachNeighbor iterates from closest bin to farthest of the neighborhood peers.
......
...@@ -49,8 +49,6 @@ func TestNeighborhoodDepth(t *testing.T) { ...@@ -49,8 +49,6 @@ func TestNeighborhoodDepth(t *testing.T) {
var ( var (
conns int32 // how many connect calls were made to the p2p mock conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{}) base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{})
peers []swarm.Address
binEight []swarm.Address
) )
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
...@@ -58,123 +56,127 @@ func TestNeighborhoodDepth(t *testing.T) { ...@@ -58,123 +56,127 @@ func TestNeighborhoodDepth(t *testing.T) {
} }
defer kad.Close() defer kad.Close()
for i := 0; i < 8; i++ { // add 2 peers in bin 8
addr := test.RandomAddressAt(base, i)
peers = append(peers, addr)
}
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
addr := test.RandomAddressAt(base, 8) addr := test.RandomAddressAt(base, 8)
binEight = append(binEight, addr) addOne(t, signer, kad, ab, addr)
}
// check empty kademlia depth is 0
kDepth(t, kad, 0)
// add two bin 8 peers, verify depth still 0 // wait for one connection
add(t, signer, kad, ab, binEight, 0, 2) waitConn(t, &conns)
}
// depth is 0
kDepth(t, kad, 0) kDepth(t, kad, 0)
var shallowPeers []swarm.Address
// add two first peers (po0,po1) // add two first peers (po0,po1)
add(t, signer, kad, ab, peers, 0, 2) for i := 0; i < 2; i++ {
addr := test.RandomAddressAt(base, i)
addOne(t, signer, kad, ab, addr)
shallowPeers = append(shallowPeers, addr)
// wait for 4 connections // wait for one connection
waitCounter(t, &conns, 4) waitConn(t, &conns)
}
// depth 2 (shallowest empty bin) for _, a := range shallowPeers {
kDepth(t, kad, 2) if !kad.IsWithinDepth(a) {
t.Fatal("expected address to be within depth")
}
}
for i := 2; i < len(peers)-1; i++ { // depth 0 - bin 0 is unsaturated
addOne(t, signer, kad, ab, peers[i]) kDepth(t, kad, 0)
for i := 2; i < 8; i++ {
addr := test.RandomAddressAt(base, i)
addOne(t, signer, kad, ab, addr)
// wait for one connection // wait for one connection
waitConn(t, &conns) waitConn(t, &conns)
}
// still zero
kDepth(t, kad, 0)
// depth is i+1 // now add peers from bin 0 and expect the depth
// to shift. the depth will be that of the shallowest
// unsaturated bin.
for i := 0; i < 7; i++ {
for j := 0; j < 3; j++ {
addr := test.RandomAddressAt(base, i)
addOne(t, signer, kad, ab, addr)
waitConn(t, &conns)
}
kDepth(t, kad, i+1) kDepth(t, kad, i+1)
} }
// the last peer in bin 7 which is empty we insert manually, // depth is 7 because bin 7 is unsaturated (1 peer)
addOne(t, signer, kad, ab, peers[len(peers)-1]) kDepth(t, kad, 7)
waitConn(t, &conns)
// depth is 8 because we have nnLowWatermark neighbors in bin 8 // expect shallow peers not in depth
kDepth(t, kad, 8)
// now add another ONE peer at depth+1, and expect the depth to still for _, a := range shallowPeers {
if kad.IsWithinDepth(a) {
t.Fatal("expected address to outside of depth")
}
}
// now add another ONE peer at depth, and expect the depth to still
// stay 8, because the counter for nnLowWatermark would be reached only at the next // stay 8, because the counter for nnLowWatermark would be reached only at the next
// depth iteration when calculating depth // depth iteration when calculating depth
addr := test.RandomAddressAt(base, 9) addr := test.RandomAddressAt(base, 8)
addOne(t, signer, kad, ab, addr) addOne(t, signer, kad, ab, addr)
waitConn(t, &conns) waitConn(t, &conns)
kDepth(t, kad, 8) kDepth(t, kad, 7)
// fill the rest up to the bin before last and check that everything works at the edges // now fill bin 7 so that it is saturated, expect depth 8
for i := 10; i < int(swarm.MaxBins)-1; i++ { for i := 0; i < 3; i++ {
addr := test.RandomAddressAt(base, i) addr := test.RandomAddressAt(base, 7)
addOne(t, signer, kad, ab, addr) addOne(t, signer, kad, ab, addr)
waitConn(t, &conns) waitConn(t, &conns)
kDepth(t, kad, i-1)
} }
kDepth(t, kad, 8)
// add a whole bunch of peers in bin 13, expect depth to stay at 13 // saturate bin 8
addr = test.RandomAddressAt(base, 8)
addOne(t, signer, kad, ab, addr)
waitConn(t, &conns)
kDepth(t, kad, 8)
var addrs []swarm.Address
// fill the rest up to the bin before last and check that everything works at the edges
for i := 9; i < int(swarm.MaxBins); i++ {
for j := 0; j < 4; j++ {
addr := test.RandomAddressAt(base, i)
addOne(t, signer, kad, ab, addr)
waitConn(t, &conns)
addrs = append(addrs, addr)
}
kDepth(t, kad, i)
}
// add a whole bunch of peers in bin 15, expect depth to stay at 15
for i := 0; i < 15; i++ { for i := 0; i < 15; i++ {
addr = test.RandomAddressAt(base, 13) addr = test.RandomAddressAt(base, 15)
addOne(t, signer, kad, ab, addr) addOne(t, signer, kad, ab, addr)
} }
waitCounter(t, &conns, 15) waitCounter(t, &conns, 15)
kDepth(t, kad, 13)
// add one at 14 - depth should be now 14
addr = test.RandomAddressAt(base, 14)
addOne(t, signer, kad, ab, addr)
kDepth(t, kad, 14)
addr2 := test.RandomAddressAt(base, 15)
addOne(t, signer, kad, ab, addr2)
kDepth(t, kad, 14)
addr3 := test.RandomAddressAt(base, 15)
addOne(t, signer, kad, ab, addr3)
kDepth(t, kad, 15) kDepth(t, kad, 15)
// now remove that peer and check that the depth is back at 14 // remove one at 14, depth should be 14
removeOne(kad, addr3) removeOne(kad, addrs[len(addrs)-5])
kDepth(t, kad, 14) kDepth(t, kad, 14)
// remove the peer at bin 1, depth should be 1 // empty bin 9 and expect depth 9
removeOne(kad, peers[1]) for i := 0; i < 4; i++ {
kDepth(t, kad, 1) removeOne(kad, addrs[i])
}
func TestIsWithinDepth(t *testing.T) {
var (
conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{})
peers []swarm.Address
)
if err := kad.Start(context.Background()); err != nil {
t.Fatal(err)
}
defer kad.Close()
for i := 0; i < 15; i++ {
addr := test.RandomAddressAt(base, i)
peers = append(peers, addr)
} }
kDepth(t, kad, 9)
add(t, signer, kad, ab, peers, 0, 15) if !kad.IsWithinDepth(addrs[0]) {
waitCounter(t, &conns, 15) t.Fatal("expected address to be within depth")
if kad.IsWithinDepth(peers[kad.NeighborhoodDepth()-1]) {
t.Fatalf("peer should NOT be in neignborhood")
} }
if !kad.IsWithinDepth(peers[kad.NeighborhoodDepth()]) {
t.Fatalf("peer should be in neignborhood")
}
} }
func TestEachNeighbor(t *testing.T) { func TestEachNeighbor(t *testing.T) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment