Skip to content

Commit

Permalink
network: peer selector expansion and PeersPhonebookArchivalNodes (#5385)
Browse files Browse the repository at this point in the history
  • Loading branch information
algonautshant authored Jun 8, 2023
1 parent fd60def commit d5593a4
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 55 deletions.
20 changes: 15 additions & 5 deletions catchup/peerSelector.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ const (
peerRank3LowBlockTime = 601
peerRank3HighBlockTime = 799

peerRankInitialFifthPriority = 800
peerRank4LowBlockTime = 801
peerRank4HighBlockTime = 999

// peerRankDownloadFailed is used for responses which could be temporary, such as missing files, or such that we don't
// have clear resolution
peerRankDownloadFailed = 900
peerRankDownloadFailed = 10000
// peerRankInvalidDownload is used for responses which are likely to be invalid - whether it's serving the wrong content
// or attempting to serve malicious content
peerRankInvalidDownload = 1000
peerRankInvalidDownload = 12000

// once a block is downloaded, the download duration is clamped into the range of [lowBlockDownloadThreshold..highBlockDownloadThreshold] and
// then mapped into the a ranking range.
Expand Down Expand Up @@ -383,8 +387,10 @@ func (ps *peerSelector) peerDownloadDurationToRank(psp *peerSelectorPeer, blockD
return downloadDurationToRank(blockDownloadDuration, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank1LowBlockTime, peerRank1HighBlockTime)
case peerRankInitialThirdPriority:
return downloadDurationToRank(blockDownloadDuration, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank2LowBlockTime, peerRank2HighBlockTime)
default: // i.e. peerRankInitialFourthPriority
case peerRankInitialFourthPriority:
return downloadDurationToRank(blockDownloadDuration, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank3LowBlockTime, peerRank3HighBlockTime)
default: // i.e. peerRankInitialFifthPriority
return downloadDurationToRank(blockDownloadDuration, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank4LowBlockTime, peerRank4HighBlockTime)
}
}

Expand Down Expand Up @@ -520,8 +526,10 @@ func lowerBound(class peerClass) int {
return peerRank1LowBlockTime
case peerRankInitialThirdPriority:
return peerRank2LowBlockTime
default: // i.e. peerRankInitialFourthPriority
case peerRankInitialFourthPriority:
return peerRank3LowBlockTime
default: // i.e. peerRankInitialFifthPriority
return peerRank4LowBlockTime
}
}

Expand All @@ -533,8 +541,10 @@ func upperBound(class peerClass) int {
return peerRank1HighBlockTime
case peerRankInitialThirdPriority:
return peerRank2HighBlockTime
default: // i.e. peerRankInitialFourthPriority
case peerRankInitialFourthPriority:
return peerRank3HighBlockTime
default: // i.e. peerRankInitialFifthPriority
return peerRank4HighBlockTime
}
}

Expand Down
50 changes: 42 additions & 8 deletions catchup/peerSelector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,12 @@ func TestPeersDownloadFailed(t *testing.T) {
if len(peerSelector.pools) == 2 {
b1orb2 := peerAddress(peerSelector.pools[0].peers[1].peer) == "b1" || peerAddress(peerSelector.pools[0].peers[1].peer) == "b2"
require.True(t, b1orb2)
require.Equal(t, peerSelector.pools[1].rank, 900)
require.Equal(t, peerSelector.pools[1].rank, peerRankDownloadFailed)
require.Equal(t, len(peerSelector.pools[1].peers), 3)
} else { // len(pools) == 3
b1orb2 := peerAddress(peerSelector.pools[1].peers[0].peer) == "b1" || peerAddress(peerSelector.pools[1].peers[0].peer) == "b2"
require.True(t, b1orb2)
require.Equal(t, peerSelector.pools[2].rank, 900)
require.Equal(t, peerSelector.pools[2].rank, peerRankDownloadFailed)
require.Equal(t, len(peerSelector.pools[2].peers), 3)
}

Expand Down Expand Up @@ -459,6 +459,7 @@ func TestPeerDownloadDurationToRank(t *testing.T) {
peers2 := []network.Peer{&mockHTTPPeer{address: "b1"}, &mockHTTPPeer{address: "b2"}}
peers3 := []network.Peer{&mockHTTPPeer{address: "c1"}, &mockHTTPPeer{address: "c2"}}
peers4 := []network.Peer{&mockHTTPPeer{address: "d1"}, &mockHTTPPeer{address: "b2"}}
peers5 := []network.Peer{&mockHTTPPeer{address: "e1"}, &mockHTTPPeer{address: "b2"}}

peerSelector := makePeerSelector(
makePeersRetrieverStub(func(options ...network.PeerOption) (peers []network.Peer) {
Expand All @@ -469,15 +470,18 @@ func TestPeerDownloadDurationToRank(t *testing.T) {
peers = append(peers, peers2...)
} else if opt == network.PeersConnectedOut {
peers = append(peers, peers3...)
} else {
} else if opt == network.PeersPhonebookArchivalNodes {
peers = append(peers, peers4...)
} else { // PeersConnectedIn
peers = append(peers, peers5...)
}
}
return
}), []peerClass{{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersConnectedIn}},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialFifthPriority, peerClass: network.PeersConnectedIn}},
)

_, err := peerSelector.getNextPeer()
Expand All @@ -490,7 +494,10 @@ func TestPeerDownloadDurationToRank(t *testing.T) {
require.Equal(t, downloadDurationToRank(500*time.Millisecond, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank2LowBlockTime, peerRank2HighBlockTime),
peerSelector.peerDownloadDurationToRank(&peerSelectorPeer{peers3[0], network.PeersConnectedOut}, 500*time.Millisecond))
require.Equal(t, downloadDurationToRank(500*time.Millisecond, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank3LowBlockTime, peerRank3HighBlockTime),
peerSelector.peerDownloadDurationToRank(&peerSelectorPeer{peers4[0], network.PeersConnectedIn}, 500*time.Millisecond))
peerSelector.peerDownloadDurationToRank(&peerSelectorPeer{peers4[0], network.PeersPhonebookArchivalNodes}, 500*time.Millisecond))
require.Equal(t, downloadDurationToRank(500*time.Millisecond, lowBlockDownloadThreshold, highBlockDownloadThreshold, peerRank4LowBlockTime, peerRank4HighBlockTime),
peerSelector.peerDownloadDurationToRank(&peerSelectorPeer{peers5[0], network.PeersConnectedIn}, 500*time.Millisecond))

}

func TestLowerUpperBounds(t *testing.T) {
Expand All @@ -499,23 +506,26 @@ func TestLowerUpperBounds(t *testing.T) {
classes := []peerClass{{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersConnectedIn}}
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersConnectedIn},
{initialRank: peerRankInitialFifthPriority, peerClass: network.PeersConnectedIn}}

require.Equal(t, peerRank0LowBlockTime, lowerBound(classes[0]))
require.Equal(t, peerRank1LowBlockTime, lowerBound(classes[1]))
require.Equal(t, peerRank2LowBlockTime, lowerBound(classes[2]))
require.Equal(t, peerRank3LowBlockTime, lowerBound(classes[3]))
require.Equal(t, peerRank4LowBlockTime, lowerBound(classes[4]))

require.Equal(t, peerRank0HighBlockTime, upperBound(classes[0]))
require.Equal(t, peerRank1HighBlockTime, upperBound(classes[1]))
require.Equal(t, peerRank2HighBlockTime, upperBound(classes[2]))
require.Equal(t, peerRank3HighBlockTime, upperBound(classes[3]))
require.Equal(t, peerRank4HighBlockTime, upperBound(classes[4]))
}

func TestFullResetRequestPenalty(t *testing.T) {
partitiontest.PartitionTest(t)

class := peerClass{initialRank: 10, peerClass: network.PeersPhonebookArchivers}
class := peerClass{initialRank: 0, peerClass: network.PeersPhonebookArchivers}
hs := makeHistoricStatus(10, class)
hs.push(5, 1, class)
require.Equal(t, 1, len(hs.requestGaps))
Expand All @@ -524,6 +534,30 @@ func TestFullResetRequestPenalty(t *testing.T) {
require.Equal(t, 0, len(hs.requestGaps))
}

// TesPenaltyBounds makes sure that the penalty does not demote the peer to a lower class,
// and resetting the penalty of a demoted peer does not promote it back
func TestPenaltyBounds(t *testing.T) {
partitiontest.PartitionTest(t)

class := peerClass{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookArchivers}
hs := makeHistoricStatus(peerHistoryWindowSize, class)
for x := 0; x < 65; x++ {
r0 := hs.push(peerRank2LowBlockTime+50, uint64(x+1), class)
require.LessOrEqual(t, peerRank2LowBlockTime, r0)
require.GreaterOrEqual(t, peerRank2HighBlockTime, r0)
}

r1 := hs.resetRequestPenalty(4, peerRankInitialThirdPriority, class)
r2 := hs.resetRequestPenalty(10, peerRankInitialThirdPriority, class)
r3 := hs.resetRequestPenalty(10, peerRankDownloadFailed, class)

// r2 is at a better rank than r1 because it has one penalty less
require.Greater(t, r1, r2)

// r3 is worse rank at peerRankDownloadFailed because it was demoted and resetRequestPenalty should not improve it
require.Equal(t, peerRankDownloadFailed, r3)
}

// TestClassUpperBound makes sure the peer rank does not exceed the class upper bound
// This was a bug where the resetRequestPenalty was not bounding the returned rank, and was having download failures.
// Initializing rankSamples to 0 makes this works, since the dropped value subtracts 0 from rankSum.
Expand Down Expand Up @@ -613,7 +647,7 @@ func TestEvictionAndUpgrade(t *testing.T) {
_, err := peerSelector.getNextPeer()
require.NoError(t, err)
for i := 0; i < 10; i++ {
if peerSelector.pools[len(peerSelector.pools)-1].rank == 900 {
if peerSelector.pools[len(peerSelector.pools)-1].rank == peerRankDownloadFailed {
require.Equal(t, 6, i)
break
}
Expand Down
40 changes: 24 additions & 16 deletions catchup/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -816,30 +816,34 @@ func createPeerSelector(net network.GossipNode, cfg config.Local, pipelineFetch
if cfg.NetAddress != "" { // Relay node
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersConnectedIn},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFifthPriority, peerClass: network.PeersConnectedIn},
}
} else {
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookRelays},
}
}
} else {
if cfg.NetAddress != "" { // Relay node
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersConnectedIn},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFifthPriority, peerClass: network.PeersPhonebookArchivers},
}
} else {
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookArchivers},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookArchivers},
}
}
}
Expand All @@ -848,26 +852,30 @@ func createPeerSelector(net network.GossipNode, cfg config.Local, pipelineFetch
if cfg.NetAddress != "" { // Relay node
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersConnectedIn},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersConnectedIn},
}
} else {
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
}
}
} else {
if cfg.NetAddress != "" { // Relay node
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersConnectedIn},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialFourthPriority, peerClass: network.PeersPhonebookRelays},
}
} else {
peerClasses = []peerClass{
{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersConnectedOut},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays},
{initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookArchivalNodes},
{initialRank: peerRankInitialThirdPriority, peerClass: network.PeersPhonebookRelays},
}
}
}
Expand Down
Loading

0 comments on commit d5593a4

Please sign in to comment.