@ -9,6 +9,7 @@ import (
"encoding/binary"
"fmt"
"math"
"math/rand"
"net"
"sync"
"time"
@ -405,89 +406,11 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre
bookSize * getSelectionPercent / 100 )
numAddresses = cmn . MinInt ( maxGetSelection , numAddresses )
selection := make ( [ ] * p2p . NetAddress , numAddresses )
oldBucketToAddrsMap := make ( map [ int ] map [ string ] struct { } )
var oldIndex int
newBucketToAddrsMap := make ( map [ int ] map [ string ] struct { } )
var newIndex int
// initialize counters used to count old and new added addresses.
// len(oldBucketToAddrsMap) cannot be used as multiple addresses can endup in the same bucket.
var oldAddressesAdded int
var newAddressesAdded int
// number of new addresses that, if possible, should be in the beginning of the selection
numRequiredNewAdd := percentageOfNum ( biasTowardsNewAddrs , numAddresses )
selectionIndex := 0
ADDRS_LOOP :
for selectionIndex < numAddresses {
// biasedTowardsOldAddrs indicates if the selection can switch to old addresses
biasedTowardsOldAddrs := selectionIndex >= numRequiredNewAdd
// An old addresses is selected if:
// - the bias is for old and old addressees are still available or,
// - there are no new addresses or all new addresses have been selected.
// numAddresses <= a.nOld + a.nNew therefore it is guaranteed that there are enough
// addresses to fill the selection
pickFromOldBucket :=
( biasedTowardsOldAddrs && oldAddressesAdded < a . nOld ) ||
a . nNew == 0 || newAddressesAdded >= a . nNew
bucket := make ( map [ string ] * knownAddress )
// loop until we pick a random non-empty bucket
for len ( bucket ) == 0 {
if pickFromOldBucket {
oldIndex = a . rand . Intn ( len ( a . bucketsOld ) )
bucket = a . bucketsOld [ oldIndex ]
} else {
newIndex = a . rand . Intn ( len ( a . bucketsNew ) )
bucket = a . bucketsNew [ newIndex ]
}
}
// pick a random index
randIndex := a . rand . Intn ( len ( bucket ) )
// loop over the map to return that index
var selectedAddr * p2p . NetAddress
for _ , ka := range bucket {
if randIndex == 0 {
selectedAddr = ka . Addr
break
}
randIndex --
}
// if we have selected the address before, restart the loop
// otherwise, record it and continue
if pickFromOldBucket {
if addrsMap , ok := oldBucketToAddrsMap [ oldIndex ] ; ok {
if _ , ok = addrsMap [ selectedAddr . String ( ) ] ; ok {
continue ADDRS_LOOP
}
} else {
oldBucketToAddrsMap [ oldIndex ] = make ( map [ string ] struct { } )
}
oldBucketToAddrsMap [ oldIndex ] [ selectedAddr . String ( ) ] = struct { } { }
oldAddressesAdded ++
} else {
if addrsMap , ok := newBucketToAddrsMap [ newIndex ] ; ok {
if _ , ok = addrsMap [ selectedAddr . String ( ) ] ; ok {
continue ADDRS_LOOP
}
} else {
newBucketToAddrsMap [ newIndex ] = make ( map [ string ] struct { } )
}
newBucketToAddrsMap [ newIndex ] [ selectedAddr . String ( ) ] = struct { } { }
newAddressesAdded ++
}
selection [ selectionIndex ] = selectedAddr
selectionIndex ++
}
// if there are no enough old addrs, will choose new addr instead.
numRequiredNewAdd := cmn . MaxInt ( percentageOfNum ( biasTowardsNewAddrs , numAddresses ) , numAddresses - a . nOld )
selection := a . randomPickAddresses ( bucketTypeNew , numRequiredNewAdd )
selection = append ( selection , a . randomPickAddresses ( bucketTypeOld , numAddresses - len ( selection ) ) ... )
return selection
}
@ -726,6 +649,44 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error {
return nil
}
func ( a * addrBook ) randomPickAddresses ( bucketType byte , num int ) [ ] * p2p . NetAddress {
var buckets [ ] map [ string ] * knownAddress
switch bucketType {
case bucketTypeNew :
buckets = a . bucketsNew
case bucketTypeOld :
buckets = a . bucketsOld
default :
panic ( "unexpected bucketType" )
}
total := 0
for _ , bucket := range buckets {
total = total + len ( bucket )
}
addresses := make ( [ ] * knownAddress , 0 , total )
for _ , bucket := range buckets {
for _ , ka := range bucket {
addresses = append ( addresses , ka )
}
}
selection := make ( [ ] * p2p . NetAddress , 0 , num )
chosenSet := make ( map [ string ] bool , num )
rand . Shuffle ( total , func ( i , j int ) {
addresses [ i ] , addresses [ j ] = addresses [ j ] , addresses [ i ]
} )
for _ , addr := range addresses {
if chosenSet [ addr . Addr . String ( ) ] {
continue
}
chosenSet [ addr . Addr . String ( ) ] = true
selection = append ( selection , addr . Addr )
if len ( selection ) >= num {
return selection
}
}
return selection
}
// Make space in the new buckets by expiring the really bad entries.
// If no bad entries are available we remove the oldest.
func ( a * addrBook ) expireNew ( bucketIdx int ) {