mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-07 16:33:08 +00:00
chore: test peer connection management (#3049)
* Make some useful consts public, add some utils. * Implement various utilities. * peer_manager reconnectPeers enhancements --------- Co-authored-by: Álex Cabeza Romero <alex93cabeza@gmail.com>
This commit is contained in:
parent
7c4a9717a6
commit
711e7db1e9
@ -8,6 +8,7 @@ import
|
|||||||
chronos,
|
chronos,
|
||||||
# chronos/timer,
|
# chronos/timer,
|
||||||
chronicles,
|
chronicles,
|
||||||
|
times,
|
||||||
libp2p/[peerstore, crypto/crypto, multiaddress]
|
libp2p/[peerstore, crypto/crypto, multiaddress]
|
||||||
|
|
||||||
from times import getTime, toUnix
|
from times import getTime, toUnix
|
||||||
@ -62,9 +63,9 @@ suite "Peer Manager":
|
|||||||
serverKey = generateSecp256k1Key()
|
serverKey = generateSecp256k1Key()
|
||||||
clientKey = generateSecp256k1Key()
|
clientKey = generateSecp256k1Key()
|
||||||
|
|
||||||
server = newTestWakuNode(serverKey, listenIp, listenPort)
|
server = newTestWakuNode(serverKey, listenIp, Port(3000))
|
||||||
serverPeerStore = server.peerManager.peerStore
|
serverPeerStore = server.peerManager.peerStore
|
||||||
client = newTestWakuNode(clientKey, listenIp, listenPort)
|
client = newTestWakuNode(clientKey, listenIp, Port(3001))
|
||||||
clientPeerStore = client.peerManager.peerStore
|
clientPeerStore = client.peerManager.peerStore
|
||||||
|
|
||||||
await allFutures(server.start(), client.start())
|
await allFutures(server.start(), client.start())
|
||||||
@ -577,77 +578,49 @@ suite "Peer Manager":
|
|||||||
Connectedness.CannotConnect
|
Connectedness.CannotConnect
|
||||||
|
|
||||||
suite "Automatic Reconnection":
|
suite "Automatic Reconnection":
|
||||||
xasyncTest "Automatic Reconnection Implementation":
|
asyncTest "Automatic Reconnection Implementation":
|
||||||
# Given two correctly initialised nodes, that are available for reconnection
|
# Given two correctly initialised nodes, that are available for reconnection
|
||||||
await server.mountRelay()
|
await server.mountRelay()
|
||||||
await client.mountRelay()
|
await client.mountRelay()
|
||||||
await client.connectToNodes(@[serverRemotePeerInfo])
|
await client.connectToNodes(@[serverRemotePeerInfo])
|
||||||
await server.switch.stop()
|
|
||||||
await client.switch.stop()
|
waitActive:
|
||||||
check:
|
clientPeerStore.get(serverPeerId).connectedness == Connectedness.Connected and
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.CanConnect
|
serverPeerStore.get(clientPeerId).connectedness == Connectedness.Connected
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
|
await client.disconnectNode(serverRemotePeerInfo)
|
||||||
|
|
||||||
|
waitActive:
|
||||||
|
clientPeerStore.get(serverPeerId).connectedness == Connectedness.CanConnect and
|
||||||
|
serverPeerStore.get(clientPeerId).connectedness == Connectedness.CanConnect
|
||||||
|
|
||||||
# When triggering the reconnection
|
# When triggering the reconnection
|
||||||
await client.peerManager.reconnectPeers(WakuRelayCodec)
|
await client.peerManager.reconnectPeers(WakuRelayCodec)
|
||||||
|
|
||||||
# Then both peers should be marked as Connected
|
# Then both peers should be marked as Connected
|
||||||
check:
|
waitActive:
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.Connected
|
clientPeerStore.get(serverPeerId).connectedness == Connectedness.Connected and
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.Connected
|
serverPeerStore.get(clientPeerId).connectedness == Connectedness.Connected
|
||||||
|
|
||||||
xasyncTest "Automatic Reconnection Implementation (With Backoff)":
|
## Now let's do the same but with backoff period
|
||||||
# Given two correctly initialised nodes, that are available for reconnection
|
await client.disconnectNode(serverRemotePeerInfo)
|
||||||
await server.mountRelay()
|
|
||||||
await client.mountRelay()
|
waitActive:
|
||||||
await client.connectToNodes(@[serverRemotePeerInfo])
|
clientPeerStore.get(serverPeerId).connectedness == Connectedness.CanConnect and
|
||||||
waitFor allFutures(server.switch.stop(), client.switch.stop())
|
serverPeerStore.get(clientPeerId).connectedness == Connectedness.CanConnect
|
||||||
waitFor allFutures(server.switch.start(), client.switch.start())
|
|
||||||
check:
|
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
|
|
||||||
# When triggering a reconnection with a backoff period
|
# When triggering a reconnection with a backoff period
|
||||||
let
|
let backoffPeriod = chronos.seconds(1)
|
||||||
backoffPeriod = 10.seconds
|
let beforeReconnect = getTime().toUnixFloat()
|
||||||
halfBackoffPeriod = 5.seconds
|
|
||||||
|
|
||||||
await client.peerManager.reconnectPeers(WakuRelayCodec, backoffPeriod)
|
await client.peerManager.reconnectPeers(WakuRelayCodec, backoffPeriod)
|
||||||
await sleepAsync(halfBackoffPeriod)
|
let reconnectDurationWithBackoffPeriod =
|
||||||
|
getTime().toUnixFloat() - beforeReconnect
|
||||||
# If the backoff period is not over, then the peers should still be marked as CanConnect
|
|
||||||
check:
|
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
|
|
||||||
# When waiting for the backoff period to be over
|
|
||||||
await sleepAsync(halfBackoffPeriod)
|
|
||||||
|
|
||||||
# Then both peers should be marked as Connected
|
|
||||||
check:
|
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.Connected
|
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.Connected
|
|
||||||
|
|
||||||
xasyncTest "Automatic Reconnection Implementation (After client restart)":
|
|
||||||
# Given two correctly initialised nodes, that are available for reconnection
|
|
||||||
await server.mountRelay()
|
|
||||||
await client.mountRelay()
|
|
||||||
await client.connectToNodes(@[serverRemotePeerInfo])
|
|
||||||
await server.switch.stop()
|
|
||||||
await client.switch.stop()
|
|
||||||
check:
|
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.CanConnect
|
|
||||||
|
|
||||||
# When triggering the reconnection, and some time for the reconnection to happen
|
|
||||||
waitFor allFutures(client.stop(), server.stop())
|
|
||||||
await allFutures(server.start(), client.start())
|
|
||||||
await sleepAsync(FUTURE_TIMEOUT_LONG)
|
|
||||||
|
|
||||||
# Then both peers should be marked as Connected
|
# Then both peers should be marked as Connected
|
||||||
check:
|
check:
|
||||||
clientPeerStore.get(serverPeerId).connectedness == Connectedness.Connected
|
clientPeerStore.get(serverPeerId).connectedness == Connectedness.Connected
|
||||||
serverPeerStore.get(clientPeerId).connectedness == Connectedness.Connected
|
serverPeerStore.get(clientPeerId).connectedness == Connectedness.Connected
|
||||||
|
reconnectDurationWithBackoffPeriod > backoffPeriod.seconds.float
|
||||||
|
|
||||||
suite "Handling Connections on Different Networks":
|
suite "Handling Connections on Different Networks":
|
||||||
# TODO: Implement after discv5 and peer manager's interaction is understood
|
# TODO: Implement after discv5 and peer manager's interaction is understood
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import testutils/unittests
|
import testutils/unittests, chronos
|
||||||
|
|
||||||
template xsuite*(name: string, body: untyped) =
|
template xsuite*(name: string, body: untyped) =
|
||||||
discard
|
discard
|
||||||
@ -27,3 +27,11 @@ template xasyncTest*(name: string, body: untyped) =
|
|||||||
template asyncTestx*(name: string, body: untyped) =
|
template asyncTestx*(name: string, body: untyped) =
|
||||||
test name:
|
test name:
|
||||||
skip()
|
skip()
|
||||||
|
|
||||||
|
template waitActive*(condition: bool) =
|
||||||
|
for i in 0 ..< 200:
|
||||||
|
if condition:
|
||||||
|
break
|
||||||
|
await sleepAsync(10)
|
||||||
|
|
||||||
|
assert condition
|
||||||
|
|||||||
@ -226,6 +226,10 @@ proc connectRelay*(
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc disconnectNode*(pm: PeerManager, peer: RemotePeerInfo) {.async.} =
|
||||||
|
let peerId = peer.peerId
|
||||||
|
await pm.switch.disconnect(peerId)
|
||||||
|
|
||||||
# Dialing should be used for just protocols that require a stream to write and read
|
# Dialing should be used for just protocols that require a stream to write and read
|
||||||
# This shall not be used to dial Relay protocols, since that would create
|
# This shall not be used to dial Relay protocols, since that would create
|
||||||
# unneccesary unused streams.
|
# unneccesary unused streams.
|
||||||
@ -560,46 +564,6 @@ proc addServicePeer*(pm: PeerManager, remotePeerInfo: RemotePeerInfo, proto: str
|
|||||||
|
|
||||||
pm.addPeer(remotePeerInfo)
|
pm.addPeer(remotePeerInfo)
|
||||||
|
|
||||||
proc reconnectPeers*(
|
|
||||||
pm: PeerManager, proto: string, backoff: chronos.Duration = chronos.seconds(0)
|
|
||||||
) {.async.} =
|
|
||||||
## Reconnect to peers registered for this protocol. This will update connectedness.
|
|
||||||
## Especially useful to resume connections from persistent storage after a restart.
|
|
||||||
|
|
||||||
trace "Reconnecting peers", proto = proto
|
|
||||||
|
|
||||||
# Proto is not persisted, we need to iterate over all peers.
|
|
||||||
for peerInfo in pm.peerStore.peers(protocolMatcher(proto)):
|
|
||||||
# Check that the peer can be connected
|
|
||||||
if peerInfo.connectedness == CannotConnect:
|
|
||||||
error "Not reconnecting to unreachable or non-existing peer",
|
|
||||||
peerId = peerInfo.peerId
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Respect optional backoff period where applicable.
|
|
||||||
let
|
|
||||||
# TODO: Add method to peerStore (eg isBackoffExpired())
|
|
||||||
disconnectTime = Moment.init(peerInfo.disconnectTime, Second) # Convert
|
|
||||||
currentTime = Moment.init(getTime().toUnix, Second)
|
|
||||||
# Current time comparable to persisted value
|
|
||||||
backoffTime = disconnectTime + backoff - currentTime
|
|
||||||
# Consider time elapsed since last disconnect
|
|
||||||
|
|
||||||
trace "Respecting backoff",
|
|
||||||
backoff = backoff,
|
|
||||||
disconnectTime = disconnectTime,
|
|
||||||
currentTime = currentTime,
|
|
||||||
backoffTime = backoffTime
|
|
||||||
|
|
||||||
# TODO: This blocks the whole function. Try to connect to another peer in the meantime.
|
|
||||||
if backoffTime > ZeroDuration:
|
|
||||||
trace "Backing off before reconnect...",
|
|
||||||
peerId = peerInfo.peerId, backoffTime = backoffTime
|
|
||||||
# We disconnected recently and still need to wait for a backoff period before connecting
|
|
||||||
await sleepAsync(backoffTime)
|
|
||||||
|
|
||||||
discard await pm.connectRelay(peerInfo)
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Dialer interface #
|
# Dialer interface #
|
||||||
####################
|
####################
|
||||||
@ -647,7 +611,7 @@ proc connectToNodes*(
|
|||||||
if nodes.len == 0:
|
if nodes.len == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
info "Dialing multiple peers", numOfPeers = nodes.len
|
info "Dialing multiple peers", numOfPeers = nodes.len, nodes = $nodes
|
||||||
|
|
||||||
var futConns: seq[Future[bool]]
|
var futConns: seq[Future[bool]]
|
||||||
var connectedPeers: seq[RemotePeerInfo]
|
var connectedPeers: seq[RemotePeerInfo]
|
||||||
@ -685,6 +649,30 @@ proc connectToNodes*(
|
|||||||
# later.
|
# later.
|
||||||
await sleepAsync(chronos.seconds(5))
|
await sleepAsync(chronos.seconds(5))
|
||||||
|
|
||||||
|
proc reconnectPeers*(
|
||||||
|
pm: PeerManager, proto: string, backoffTime: chronos.Duration = chronos.seconds(0)
|
||||||
|
) {.async.} =
|
||||||
|
## Reconnect to peers registered for this protocol. This will update connectedness.
|
||||||
|
## Especially useful to resume connections from persistent storage after a restart.
|
||||||
|
|
||||||
|
debug "Reconnecting peers", proto = proto
|
||||||
|
|
||||||
|
# Proto is not persisted, we need to iterate over all peers.
|
||||||
|
for peerInfo in pm.peerStore.peers(protocolMatcher(proto)):
|
||||||
|
# Check that the peer can be connected
|
||||||
|
if peerInfo.connectedness == CannotConnect:
|
||||||
|
error "Not reconnecting to unreachable or non-existing peer",
|
||||||
|
peerId = peerInfo.peerId
|
||||||
|
continue
|
||||||
|
|
||||||
|
if backoffTime > ZeroDuration:
|
||||||
|
debug "Backing off before reconnect",
|
||||||
|
peerId = peerInfo.peerId, backoffTime = backoffTime
|
||||||
|
# We disconnected recently and still need to wait for a backoff period before connecting
|
||||||
|
await sleepAsync(backoffTime)
|
||||||
|
|
||||||
|
await pm.connectToNodes(@[peerInfo])
|
||||||
|
|
||||||
proc connectedPeers*(pm: PeerManager, protocol: string): (seq[PeerId], seq[PeerId]) =
|
proc connectedPeers*(pm: PeerManager, protocol: string): (seq[PeerId], seq[PeerId]) =
|
||||||
## Returns the peerIds of physical connections (in and out)
|
## Returns the peerIds of physical connections (in and out)
|
||||||
## containing at least one stream with the given protocol.
|
## containing at least one stream with the given protocol.
|
||||||
|
|||||||
@ -198,6 +198,9 @@ proc connectToNodes*(
|
|||||||
# NOTE Connects to the node without a give protocol, which automatically creates streams for relay
|
# NOTE Connects to the node without a give protocol, which automatically creates streams for relay
|
||||||
await peer_manager.connectToNodes(node.peerManager, nodes, source = source)
|
await peer_manager.connectToNodes(node.peerManager, nodes, source = source)
|
||||||
|
|
||||||
|
proc disconnectNode*(node: WakuNode, remotePeer: RemotePeerInfo) {.async.} =
|
||||||
|
await peer_manager.disconnectNode(node.peerManager, remotePeer)
|
||||||
|
|
||||||
## Waku Sync
|
## Waku Sync
|
||||||
|
|
||||||
proc mountWakuSync*(
|
proc mountWakuSync*(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user