Peer scorer: fix weighted sorting (#6981)

* peer scorer: fix weighted sorting
* Merge refs/heads/master into minor-fixes-to-peer-scorer
* Merge refs/heads/master into minor-fixes-to-peer-scorer
This commit is contained in:
Victor Farazdagi 2020-08-13 12:36:50 +03:00 committed by GitHub
parent 7611f6d84a
commit 0c94298847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 28 deletions

View File

@ -15,20 +15,20 @@ import (
const (
// DefaultBlockProviderProcessedBatchWeight is a default reward weight of a processed batch of blocks.
DefaultBlockProviderProcessedBatchWeight = float64(0.05)
DefaultBlockProviderProcessedBatchWeight = float64(0.1)
// DefaultBlockProviderProcessedBlocksCap defines default value for processed blocks cap.
// e.g. 20 * 64 := 20 batches of size 64 (with 0.05 per batch reward, 20 batches result in score of 1.0).
DefaultBlockProviderProcessedBlocksCap = uint64(20 * 64)
DefaultBlockProviderProcessedBlocksCap = uint64(10 * 64)
// DefaultBlockProviderDecayInterval defines how often the decaying routine is called.
DefaultBlockProviderDecayInterval = 30 * time.Second
// DefaultBlockProviderDecay defines default blocks that are to be subtracted from stats on each
// decay interval. Effectively, this param provides minimum expected performance for a peer to remain
// high scorer.
DefaultBlockProviderDecay = uint64(5 * 64)
DefaultBlockProviderDecay = uint64(1 * 64)
// DefaultBlockProviderStalePeerRefreshInterval defines default interval at which peers should be given
// opportunity to provide blocks (their score gets boosted, up until they are selected for
// fetching).
DefaultBlockProviderStalePeerRefreshInterval = 1 * time.Minute
DefaultBlockProviderStalePeerRefreshInterval = 5 * time.Minute
)
// BlockProviderScorer represents block provider scoring service.
@ -209,14 +209,15 @@ func (s *BlockProviderScorer) WeightSorted(
nextPID := func(weights map[peer.ID]float64) peer.ID {
totalWeight := 0
for _, w := range weights {
totalWeight += int(w)
// Factor by 100, to allow weights in (0; 1) range.
totalWeight += int(w * 100)
}
if totalWeight <= 0 {
return ""
}
rnd := r.Intn(totalWeight)
for pid, w := range weights {
rnd -= int(w)
rnd -= int(w * 100)
if rnd < 0 {
return pid
}

View File

@ -51,8 +51,9 @@ func TestPeerScorer_BlockProvider_Score(t *testing.T) {
{
name: "boost score of stale peer",
update: func(scorer *peers.BlockProviderScorer) {
batchWeight := scorer.Params().ProcessedBatchWeight
scorer.IncrementProcessedBlocks("peer1", batchSize*3)
assert.Equal(t, 0.05*3, scorer.Score("peer1"), "Unexpected score")
assert.Equal(t, roundScore(batchWeight*3), scorer.Score("peer1"), "Unexpected score")
scorer.Touch("peer1", roughtime.Now().Add(-1*scorer.Params().StalePeerRefreshInterval))
},
check: func(scorer *peers.BlockProviderScorer) {
@ -85,23 +86,26 @@ func TestPeerScorer_BlockProvider_Score(t *testing.T) {
scorer.IncrementProcessedBlocks("peer1", batchSize)
},
check: func(scorer *peers.BlockProviderScorer) {
assert.Equal(t, roundScore(0.05), scorer.Score("peer1"), "Unexpected score")
batchWeight := scorer.Params().ProcessedBatchWeight
assert.Equal(t, roundScore(batchWeight), scorer.Score("peer1"), "Unexpected score")
},
},
{
name: "multiple batches",
update: func(scorer *peers.BlockProviderScorer) {
scorer.IncrementProcessedBlocks("peer1", batchSize*13)
scorer.IncrementProcessedBlocks("peer1", batchSize*7)
},
check: func(scorer *peers.BlockProviderScorer) {
assert.Equal(t, roundScore(0.05*13), scorer.Score("peer1"), "Unexpected score")
batchWeight := scorer.Params().ProcessedBatchWeight
assert.Equal(t, roundScore(batchWeight*7), scorer.Score("peer1"), "Unexpected score")
},
},
{
name: "maximum score cap",
update: func(scorer *peers.BlockProviderScorer) {
batchWeight := scorer.Params().ProcessedBatchWeight
scorer.IncrementProcessedBlocks("peer1", batchSize*2)
assert.Equal(t, roundScore(0.05*2), scorer.Score("peer1"), "Unexpected score")
assert.Equal(t, roundScore(batchWeight*2), scorer.Score("peer1"), "Unexpected score")
scorer.IncrementProcessedBlocks("peer1", scorer.Params().ProcessedBlocksCap)
},
check: func(scorer *peers.BlockProviderScorer) {
@ -116,9 +120,7 @@ func TestPeerScorer_BlockProvider_Score(t *testing.T) {
peerStatuses := peers.NewStatus(ctx, &peers.StatusConfig{
PeerLimit: 30,
ScorerParams: &peers.PeerScorerConfig{
BlockProviderScorerConfig: &peers.BlockProviderScorerConfig{
ProcessedBatchWeight: 0.05,
},
BlockProviderScorerConfig: &peers.BlockProviderScorerConfig{},
},
})
scorer := peerStatuses.Scorers().BlockProviderScorer()
@ -150,7 +152,7 @@ func TestPeerScorer_BlockProvider_WeightSorted(t *testing.T) {
peerStatuses := peers.NewStatus(ctx, &peers.StatusConfig{
ScorerParams: &peers.PeerScorerConfig{
BlockProviderScorerConfig: &peers.BlockProviderScorerConfig{
ProcessedBatchWeight: 1,
ProcessedBatchWeight: 0.01,
},
},
})

View File

@ -109,8 +109,7 @@ func TestPeerScorer_PeerScorerManager_Score(t *testing.T) {
Threshold: 5,
},
BlockProviderScorerConfig: &peers.BlockProviderScorerConfig{
ProcessedBatchWeight: 0.05,
Decay: 64,
Decay: 64,
},
},
})
@ -156,6 +155,7 @@ func TestPeerScorer_PeerScorerManager_Score(t *testing.T) {
s, pids := setupScorer()
s1 := s.BlockProviderScorer()
zeroScore := s.BlockProviderScorer().MaxScore()
batchWeight := s1.Params().ProcessedBatchWeight
// Partial batch.
s1.IncrementProcessedBlocks("peer1", batchSize/4)
@ -163,26 +163,26 @@ func TestPeerScorer_PeerScorerManager_Score(t *testing.T) {
// Single batch.
s1.IncrementProcessedBlocks("peer1", batchSize)
assert.DeepEqual(t, pack(s, 0.05, zeroScore, zeroScore), peerScores(s, pids), "Unexpected scores")
assert.DeepEqual(t, pack(s, batchWeight, zeroScore, zeroScore), peerScores(s, pids), "Unexpected scores")
// Multiple batches.
s1.IncrementProcessedBlocks("peer2", batchSize*4)
assert.DeepEqual(t, pack(s, 0.05, 0.05*4, zeroScore), peerScores(s, pids), "Unexpected scores")
assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, zeroScore), peerScores(s, pids), "Unexpected scores")
// Partial batch.
s1.IncrementProcessedBlocks("peer3", batchSize/2)
assert.DeepEqual(t, pack(s, 0.05, 0.05*4, 0), peerScores(s, pids), "Unexpected scores")
assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, 0), peerScores(s, pids), "Unexpected scores")
// See effect of decaying.
assert.Equal(t, batchSize+batchSize/4, s1.ProcessedBlocks("peer1"))
assert.Equal(t, batchSize*4, s1.ProcessedBlocks("peer2"))
assert.Equal(t, batchSize/2, s1.ProcessedBlocks("peer3"))
assert.DeepEqual(t, pack(s, 0.05, 0.05*4, 0), peerScores(s, pids), "Unexpected scores")
assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, 0), peerScores(s, pids), "Unexpected scores")
s1.Decay()
assert.Equal(t, batchSize/4, s1.ProcessedBlocks("peer1"))
assert.Equal(t, batchSize*3, s1.ProcessedBlocks("peer2"))
assert.Equal(t, uint64(0), s1.ProcessedBlocks("peer3"))
assert.DeepEqual(t, pack(s, 0, 0.05*3, 0), peerScores(s, pids), "Unexpected scores")
assert.DeepEqual(t, pack(s, 0, batchWeight*3, 0), peerScores(s, pids), "Unexpected scores")
})
t.Run("overall score", func(t *testing.T) {
@ -190,20 +190,21 @@ func TestPeerScorer_PeerScorerManager_Score(t *testing.T) {
s, _ := setupScorer()
s1 := s.BlockProviderScorer()
s2 := s.BadResponsesScorer()
batchWeight := s1.Params().ProcessedBatchWeight
s1.IncrementProcessedBlocks("peer1", batchSize*10)
assert.Equal(t, roundScore(0.05*10), s1.Score("peer1"))
s1.IncrementProcessedBlocks("peer1", batchSize*5)
assert.Equal(t, roundScore(batchWeight*5), s1.Score("peer1"))
// Now, adjust score by introducing penalty for bad responses.
s2.Increment("peer1")
s2.Increment("peer1")
assert.Equal(t, -0.4, s2.Score("peer1"), "Unexpected bad responses score")
assert.Equal(t, roundScore(0.05*10), s1.Score("peer1"), "Unexpected block provider score")
assert.Equal(t, roundScore(0.05*10-0.4), s.Score("peer1"), "Unexpected overall score")
assert.Equal(t, roundScore(batchWeight*5), s1.Score("peer1"), "Unexpected block provider score")
assert.Equal(t, roundScore(batchWeight*5-0.4), s.Score("peer1"), "Unexpected overall score")
// If peer continues to misbehave, score becomes negative.
s2.Increment("peer1")
assert.Equal(t, -0.6, s2.Score("peer1"), "Unexpected bad responses score")
assert.Equal(t, roundScore(0.05*10), s1.Score("peer1"), "Unexpected block provider score")
assert.Equal(t, -0.1, s.Score("peer1"), "Unexpected overall score")
assert.Equal(t, roundScore(batchWeight*5), s1.Score("peer1"), "Unexpected block provider score")
assert.Equal(t, roundScore(batchWeight*5-0.6), s.Score("peer1"), "Unexpected overall score")
})
}