Gossipsub: Rebalance mesh immediately when peer sub/unsub (#719)

This commit is contained in:
Tanguy 2022-09-02 10:24:54 +02:00 committed by GitHub
parent 543358b262
commit 3ffc03ed16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 61 deletions

View File

@ -435,6 +435,13 @@ method rpcHandler*(g: GossipSub,
if rpcMsg.control.isSome(): if rpcMsg.control.isSome():
g.handleControl(peer, rpcMsg.control.unsafeGet()) g.handleControl(peer, rpcMsg.control.unsafeGet())
# Now, check subscription to update the meshes if required
for i in 0..<min(g.topicsHigh, rpcMsg.subscriptions.len):
let topic = rpcMsg.subscriptions[i].topic
if topic in g.topics and g.mesh.peers(topic) < g.parameters.dLow:
# rebalance but don't update metrics here, we do that only in the heartbeat
g.rebalanceMesh(topic, metrics = nil)
g.updateMetrics(rpcMsg) g.updateMetrics(rpcMsg)
method onTopicSubscription*(g: GossipSub, topic: string, subscribed: bool) = method onTopicSubscription*(g: GossipSub, topic: string, subscribed: bool) =

View File

@ -32,18 +32,11 @@ proc `$`(peer: PubSubPeer): string = shortLog(peer)
proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} = proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} =
if sender == receiver: if sender == receiver:
return return
# turn things deterministic let timeout = Moment.now() + 5.seconds
let fsub = GossipSub(sender)
# this is for testing purposes only # this is for testing purposes only
# peers can be inside `mesh` and `fanout`, not just `gossipsub` # peers can be inside `mesh` and `fanout`, not just `gossipsub`
var ceil = 15
let fsub = GossipSub(sender)
let ev = newAsyncEvent()
fsub.heartbeatEvents.add(ev)
# await first heartbeat
await ev.wait()
ev.clear()
while (not fsub.gossipsub.hasKey(key) or while (not fsub.gossipsub.hasKey(key) or
not fsub.gossipsub.hasPeerId(key, receiver.peerInfo.peerId)) and not fsub.gossipsub.hasPeerId(key, receiver.peerInfo.peerId)) and
(not fsub.mesh.hasKey(key) or (not fsub.mesh.hasKey(key) or
@ -52,23 +45,19 @@ proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} =
not fsub.fanout.hasPeerId(key , receiver.peerInfo.peerId)): not fsub.fanout.hasPeerId(key , receiver.peerInfo.peerId)):
trace "waitSub sleeping..." trace "waitSub sleeping..."
# await more heartbeats # await
await ev.wait() await sleepAsync(5.milliseconds)
ev.clear() doAssert Moment.now() < timeout, "waitSub timeout!"
dec ceil template tryPublish(call: untyped, require: int, wait = 10.milliseconds, timeout = 5.seconds): untyped =
doAssert(ceil > 0, "waitSub timeout!")
template tryPublish(call: untyped, require: int, wait: Duration = 1.seconds, times: int = 10): untyped =
var var
limit = times expiration = Moment.now() + timeout
pubs = 0 pubs = 0
while pubs < require and limit > 0: while pubs < require and Moment.now() < expiration:
pubs = pubs + call pubs = pubs + call
await sleepAsync(wait) await sleepAsync(wait)
limit.dec()
if limit == 0: doAssert pubs >= require, "Failed to publish!"
doAssert(false, "Failed to publish!")
suite "GossipSub": suite "GossipSub":
teardown: teardown:
@ -343,15 +332,15 @@ suite "GossipSub":
await subscribeNodes(nodes) await subscribeNodes(nodes)
nodes[1].subscribe("foobar", handler) nodes[1].subscribe("foobar", handler)
await sleepAsync(10.seconds)
let gossip1 = GossipSub(nodes[0]) let gossip1 = GossipSub(nodes[0])
let gossip2 = GossipSub(nodes[1]) let gossip2 = GossipSub(nodes[1])
check: check await checkExpiring(
"foobar" in gossip2.topics "foobar" in gossip2.topics and
"foobar" in gossip1.gossipsub "foobar" in gossip1.gossipsub and
gossip1.gossipsub.hasPeerId("foobar", gossip2.peerInfo.peerId) gossip1.gossipsub.hasPeerId("foobar", gossip2.peerInfo.peerId)
)
await allFuturesThrowing( await allFuturesThrowing(
nodes[0].switch.stop(), nodes[0].switch.stop(),
@ -706,7 +695,7 @@ suite "GossipSub":
tryPublish await wait(nodes[0].publish("foobar", tryPublish await wait(nodes[0].publish("foobar",
toBytes("from node " & toBytes("from node " &
$nodes[0].peerInfo.peerId)), $nodes[0].peerInfo.peerId)),
1.minutes), 1, 5.seconds 1.minutes), 1
await wait(seenFut, 2.minutes) await wait(seenFut, 2.minutes)
check: seen.len >= runs check: seen.len >= runs
@ -756,7 +745,7 @@ suite "GossipSub":
tryPublish await wait(nodes[0].publish("foobar", tryPublish await wait(nodes[0].publish("foobar",
toBytes("from node " & toBytes("from node " &
$nodes[0].peerInfo.peerId)), $nodes[0].peerInfo.peerId)),
1.minutes), 1, 5.seconds 1.minutes), 1
await wait(seenFut, 5.minutes) await wait(seenFut, 5.minutes)
check: seen.len >= runs check: seen.len >= runs

View File

@ -28,18 +28,11 @@ import ../helpers
proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} = proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} =
if sender == receiver: if sender == receiver:
return return
# turn things deterministic let timeout = Moment.now() + 5.seconds
let fsub = GossipSub(sender)
# this is for testing purposes only # this is for testing purposes only
# peers can be inside `mesh` and `fanout`, not just `gossipsub` # peers can be inside `mesh` and `fanout`, not just `gossipsub`
var ceil = 15
let fsub = GossipSub(sender)
let ev = newAsyncEvent()
fsub.heartbeatEvents.add(ev)
# await first heartbeat
await ev.wait()
ev.clear()
while (not fsub.gossipsub.hasKey(key) or while (not fsub.gossipsub.hasKey(key) or
not fsub.gossipsub.hasPeerId(key, receiver.peerInfo.peerId)) and not fsub.gossipsub.hasPeerId(key, receiver.peerInfo.peerId)) and
(not fsub.mesh.hasKey(key) or (not fsub.mesh.hasKey(key) or
@ -48,23 +41,19 @@ proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} =
not fsub.fanout.hasPeerId(key , receiver.peerInfo.peerId)): not fsub.fanout.hasPeerId(key , receiver.peerInfo.peerId)):
trace "waitSub sleeping..." trace "waitSub sleeping..."
# await more heartbeats # await
await ev.wait() await sleepAsync(5.milliseconds)
ev.clear() doAssert Moment.now() < timeout, "waitSub timeout!"
dec ceil template tryPublish(call: untyped, require: int, wait = 10.milliseconds, timeout = 10.seconds): untyped =
doAssert(ceil > 0, "waitSub timeout!")
template tryPublish(call: untyped, require: int, wait: Duration = 1.seconds, times: int = 10): untyped =
var var
limit = times expiration = Moment.now() + timeout
pubs = 0 pubs = 0
while pubs < require and limit > 0: while pubs < require and Moment.now() < expiration:
pubs = pubs + call pubs = pubs + call
await sleepAsync(wait) await sleepAsync(wait)
limit.dec()
if limit == 0: doAssert pubs >= require, "Failed to publish!"
doAssert(false, "Failed to publish!")
suite "GossipSub": suite "GossipSub":
teardown: teardown:
@ -314,7 +303,7 @@ suite "GossipSub":
tryPublish await wait(nodes[0].publish("foobar", tryPublish await wait(nodes[0].publish("foobar",
toBytes("from node " & toBytes("from node " &
$nodes[0].peerInfo.peerId)), $nodes[0].peerInfo.peerId)),
1.minutes), 1, 5.seconds 1.minutes), 1, 5.seconds, 3.minutes
await wait(seenFut, 5.minutes) await wait(seenFut, 5.minutes)
check: seen.len >= runs check: seen.len >= runs
@ -337,11 +326,9 @@ suite "GossipSub":
# Waiting 2 heartbeats # Waiting 2 heartbeats
for _ in 0..1: for _ in 0..1:
for i in 0..<runs: let evnt = newAsyncEvent()
if i mod 3 == 0: GossipSub(nodes[0]).heartbeatEvents &= evnt
let evnt = newAsyncEvent() await evnt.wait()
GossipSub(nodes[i]).heartbeatEvents &= evnt
await evnt.wait()
# ensure peer stats are stored properly and kept properly # ensure peer stats are stored properly and kept properly
check: check:
@ -359,11 +346,9 @@ suite "GossipSub":
# Waiting 2 heartbeats # Waiting 2 heartbeats
for _ in 0..1: for _ in 0..1:
for i in 0..<runs: let evnt = newAsyncEvent()
if i mod 3 == 0: GossipSub(nodes[0]).heartbeatEvents &= evnt
let evnt = newAsyncEvent() await evnt.wait()
GossipSub(nodes[i]).heartbeatEvents &= evnt
await evnt.wait()
# ensure peer stats are stored properly and kept properly # ensure peer stats are stored properly and kept properly
check: check: