Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

network: peer selector expansion and PeersPhonebookArchivalNodes #5385

Merged
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},
bbroder-algo marked this conversation as resolved.
Show resolved Hide resolved
{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