feat: implement weighted random peer selection for load balancing

Use probabilistic distribution based on peer quality scores, giving all peers
opportunity while favoring better-performing ones. Selection probability is
inversely proportional to score.
This commit is contained in:
Chrysostomos Nanakos 2025-09-15 15:49:05 +03:00
parent 7cb4b31557
commit 524e93090d
No known key found for this signature in database

View File

@ -836,19 +836,39 @@ proc blockexcTaskRunner(self: BlockExcEngine) {.async: (raises: []).} =
info "Exiting blockexc task runner"
proc selectRandom*(peers: seq[BlockExcPeerCtx]): BlockExcPeerCtx {.gcsafe, raises: [].} =
proc selectRandom*(
peers: seq[BlockExcPeerCtx]
): BlockExcPeerCtx {.gcsafe, raises: [].} =
if peers.len == 1:
return peers[0]
try:
let sortedPeers = peers.sortedByIt(it.blocksRequested.len)
proc evalPeerScore(peer: BlockExcPeerCtx): float =
let
loadPenalty = peer.blocksRequested.len.float * 2.0
successRate =
if peer.exchanged > 0:
peer.exchanged.float / (peer.exchanged + peer.blocksRequested.len).float
else:
0.5
failurePenalty = (1.0 - successRate) * 5.0
return loadPenalty + failurePenalty
# Take top 40% of least loaded peers (minimum 3, maximum 8)
let selectionPoolSize = max(3, min(8, int(float(peers.len) * 0.4)))
let candidates = sortedPeers[0 ..< min(selectionPoolSize, sortedPeers.len)]
return Rng.instance.sample(candidates)
except CatchableError:
return Rng.instance.sample(peers)
let
scores = peers.mapIt(evalPeerScore(it))
maxScore = scores.max() + 1.0
weights = scores.mapIt(maxScore - it)
var totalWeight = 0.0
for w in weights:
totalWeight += w
var r = rand(totalWeight)
for i, weight in weights:
r -= weight
if r <= 0.0:
return peers[i]
return peers[^1]
proc new*(
T: type BlockExcEngine,