Skip to content

Commit d7c403e

Browse files
committed
feat: add stopgap "adaptive" refresh
1 parent b274b29 commit d7c403e

File tree

2 files changed

+36
-12
lines changed

2 files changed

+36
-12
lines changed

codex/blockexchange/engine/engine.nim

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,14 @@ proc refreshBlockKnowledge(
175175
self: BlockExcEngine, peer: BlockExcPeerCtx
176176
) {.async: (raises: [CancelledError]).} =
177177
if self.pendingBlocks.wantListLen > 0:
178-
let cids = toSeq(self.pendingBlocks.wantList)
179-
trace "Sending our want list to a peer", peer = peer.id, length = cids.len
180-
await self.network.request.sendWantList(peer.id, cids, full = true)
178+
# We send only blocks that the peer hasn't already told us that they already have.
179+
let
180+
peerHave = peer.peerHave
181+
toAsk = self.pendingBlocks.wantList.toSeq.filterIt(it notin peerHave)
182+
183+
if toAsk.len > 0:
184+
trace "Sending want list to a peer", peer = peer.id, length = toAsk.len
185+
await self.network.request.sendWantList(peer.id, toAsk, full = true)
181186

182187
proc refreshBlockKnowledge(self: BlockExcEngine) {.async: (raises: [CancelledError]).} =
183188
for peer in self.peers.peers.values.toSeq:
@@ -189,15 +194,13 @@ proc refreshBlockKnowledge(self: BlockExcEngine) {.async: (raises: [CancelledErr
189194
# want list in the coarsest way possible instead of over many
190195
# small updates.
191196
#
197+
if peer.refreshInProgress:
198+
trace "Peer refresh in progress", peer = peer.id
199+
continue
200+
192201
# In dynamic swarms, staleness will dominate latency.
193202
if peer.lastRefresh < self.pendingBlocks.lastInclusion or peer.isKnowledgeStale:
194-
# FIXME: we update the lastRefresh before actually refreshing because otherwise
195-
# a slow peer will be bombarded with requests. If the request does fail or the
196-
# peer does not reply, a retrying block will eventually issue this again. This
197-
# is a complex and convoluted flow - ideally we should simply be tracking this
198-
# request and retrying it on the absence of a response, eventually disconnecting
199-
# the peer if it consistently fails to respond.
200-
peer.refreshed()
203+
peer.refreshRequested()
201204
# TODO: optimize this by keeping track of what was sent and sending deltas.
202205
# This should allow us to run much more frequent refreshes, and be way more
203206
# efficient about it.
@@ -393,6 +396,8 @@ proc blockPresenceHandler*(
393396
if peerCtx.isNil:
394397
return
395398

399+
peerCtx.refreshReplied()
400+
396401
for blk in blocks:
397402
if presence =? Presence.init(blk):
398403
peerCtx.setPresence(presence)

codex/blockexchange/peers/peercontext.nim

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ import ../../logutils
2525

2626
export payments, nitro
2727

28+
const
29+
MinRefreshInterval = 5.seconds
30+
MaxRefreshBackoff = 36 # 3 minutes
31+
2832
type BlockExcPeerCtx* = ref object of RootObj
2933
id*: PeerId
3034
blocks*: Table[BlockAddress, Presence] # remote peer have list including price
3135
wantedBlocks*: HashSet[BlockAddress] # blocks that the peer wants
3236
exchanged*: int # times peer has exchanged with us
37+
refreshInProgress*: bool # indicates if a refresh is in progress
3338
lastRefresh*: Moment # last time we refreshed our knowledge of the blocks this peer has
39+
refreshBackoff*: int = 1 # backoff factor for refresh requests
3440
account*: ?Account # ethereum account of this peer
3541
paymentChannel*: ?ChannelId # payment channel id
3642
blocksSent*: HashSet[BlockAddress] # blocks sent to peer
@@ -39,7 +45,7 @@ type BlockExcPeerCtx* = ref object of RootObj
3945
activityTimeout*: Duration
4046

4147
proc isKnowledgeStale*(self: BlockExcPeerCtx): bool =
42-
self.lastRefresh + 5.minutes < Moment.now()
48+
self.lastRefresh + self.refreshBackoff * MinRefreshInterval < Moment.now()
4349

4450
proc isBlockSent*(self: BlockExcPeerCtx, address: BlockAddress): bool =
4551
address in self.blocksSent
@@ -50,8 +56,18 @@ proc markBlockAsSent*(self: BlockExcPeerCtx, address: BlockAddress) =
5056
proc markBlockAsNotSent*(self: BlockExcPeerCtx, address: BlockAddress) =
5157
self.blocksSent.excl(address)
5258

53-
proc refreshed*(self: BlockExcPeerCtx) =
59+
proc refreshRequested*(self: BlockExcPeerCtx) =
60+
trace "Refresh requested for peer", peer = self.id, backoff = self.refreshBackoff
61+
self.refreshInProgress = true
62+
self.lastRefresh = Moment.now()
63+
64+
proc refreshReplied*(self: BlockExcPeerCtx) =
65+
self.refreshInProgress = false
5466
self.lastRefresh = Moment.now()
67+
self.refreshBackoff = min(self.refreshBackoff * 2, MaxRefreshBackoff)
68+
69+
proc havesUpdated(self: BlockExcPeerCtx) =
70+
self.refreshBackoff = 1
5571

5672
proc peerHave*(self: BlockExcPeerCtx): HashSet[BlockAddress] =
5773
# XXX: this is ugly an inefficient, but since those will typically
@@ -63,6 +79,9 @@ proc contains*(self: BlockExcPeerCtx, address: BlockAddress): bool =
6379
address in self.blocks
6480

6581
func setPresence*(self: BlockExcPeerCtx, presence: Presence) =
82+
if presence.address notin self.blocks:
83+
self.havesUpdated()
84+
6685
self.blocks[presence.address] = presence
6786

6887
func cleanPresence*(self: BlockExcPeerCtx, addresses: seq[BlockAddress]) =

0 commit comments

Comments
 (0)