mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 06:42:16 +00:00
.
This commit is contained in:
parent
c92fa8a08e
commit
4e0010a4e9
@ -8,6 +8,7 @@ import (
|
|||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
crand "crypto/rand" // for seeding
|
crand "crypto/rand" // for seeding
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -15,10 +16,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* AddrManager - concurrency safe peer address manager */
|
/* AddrManager - concurrency safe peer address manager */
|
||||||
type AddrManager struct {
|
type AddrManager struct {
|
||||||
|
mtx sync.Mutex
|
||||||
rand *rand.Rand
|
rand *rand.Rand
|
||||||
key [32]byte
|
key [32]byte
|
||||||
addrIndex map[string]*KnownAddress // addr.String() -> KnownAddress
|
addrIndex map[string]*KnownAddress // addr.String() -> KnownAddress
|
||||||
@ -27,10 +31,11 @@ type AddrManager struct {
|
|||||||
started int32
|
started int32
|
||||||
shutdown int32
|
shutdown int32
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
quit chan bool
|
quit chan struct{}
|
||||||
nOld int
|
nOld int
|
||||||
nNew int
|
nNew int
|
||||||
localAddresses map[string]*localAddress
|
localAddresses map[string]*localAddress
|
||||||
|
filePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -86,16 +91,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Use Start to begin processing asynchronous address updates.
|
// Use Start to begin processing asynchronous address updates.
|
||||||
func NewAddrManager() *AddrManager {
|
func NewAddrManager(filePath string) *AddrManager {
|
||||||
am := AddrManager{
|
am := AddrManager{
|
||||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||||
quit: make(chan bool),
|
quit: make(chan struct{}),
|
||||||
localAddresses: make(map[string]*localAddress),
|
localAddresses: make(map[string]*localAddress),
|
||||||
|
filePath: filePath,
|
||||||
}
|
}
|
||||||
am.init()
|
am.init()
|
||||||
return &am
|
return &am
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When modifying this, don't forget to update loadFromFile()
|
||||||
func (a *AddrManager) init() {
|
func (a *AddrManager) init() {
|
||||||
a.addrIndex = make(map[string]*KnownAddress)
|
a.addrIndex = make(map[string]*KnownAddress)
|
||||||
io.ReadFull(crand.Reader, a.key[:])
|
io.ReadFull(crand.Reader, a.key[:])
|
||||||
@ -110,8 +117,8 @@ func (a *AddrManager) init() {
|
|||||||
func (a *AddrManager) Start() {
|
func (a *AddrManager) Start() {
|
||||||
if atomic.AddInt32(&a.started, 1) != 1 { return }
|
if atomic.AddInt32(&a.started, 1) != 1 { return }
|
||||||
amgrLog.Trace("Starting address manager")
|
amgrLog.Trace("Starting address manager")
|
||||||
|
a.loadFromFile(a.filePath)
|
||||||
a.wg.Add(1)
|
a.wg.Add(1)
|
||||||
a.loadPeers()
|
|
||||||
go a.addressHandler()
|
go a.addressHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +130,7 @@ func (a *AddrManager) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddrManager) AddAddress(addr *NetAddress, src *NetAddress) {
|
func (a *AddrManager) AddAddress(addr *NetAddress, src *NetAddress) {
|
||||||
// XXX use a channel for concurrency
|
a.mtx.Lock(); defer a.mtx.Unlock()
|
||||||
a.addAddress(addr, src)
|
a.addAddress(addr, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,12 +139,15 @@ func (a *AddrManager) NeedMoreAddresses() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddrManager) NumAddresses() int {
|
func (a *AddrManager) NumAddresses() int {
|
||||||
|
a.mtx.Lock(); defer a.mtx.Unlock()
|
||||||
return a.nOld + a.nNew
|
return a.nOld + a.nNew
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick a new address to connect to.
|
// Pick a new address to connect to.
|
||||||
func (a *AddrManager) PickAddress(class string, newBias int) *KnownAddress {
|
func (a *AddrManager) PickAddress(class string, newBias int) *KnownAddress {
|
||||||
if a.NumAddresses() == 0 { return nil }
|
a.mtx.Lock(); defer a.mtx.Unlock()
|
||||||
|
|
||||||
|
if a.nOld == 0 && a.nNew == 0 { return nil }
|
||||||
if newBias > 100 { newBias = 100 }
|
if newBias > 100 { newBias = 100 }
|
||||||
if newBias < 0 { newBias = 0 }
|
if newBias < 0 { newBias = 0 }
|
||||||
|
|
||||||
@ -146,28 +156,110 @@ func (a *AddrManager) PickAddress(class string, newBias int) *KnownAddress {
|
|||||||
newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)
|
newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)
|
||||||
|
|
||||||
if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation {
|
if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation {
|
||||||
// Old entry.
|
// pick random Old bucket.
|
||||||
// XXX
|
var bucket []*KnownAddress = nil
|
||||||
|
for len(bucket) == 0 {
|
||||||
|
bucket = a.addrOld[a.rand.Intn(len(a.addrOld))]
|
||||||
|
}
|
||||||
|
// pick a random ka from bucket.
|
||||||
|
return bucket[a.rand.Intn(len(bucket))]
|
||||||
} else {
|
} else {
|
||||||
// New entry.
|
// pick random New bucket.
|
||||||
// XXX
|
var bucket map[string]*KnownAddress = nil
|
||||||
|
for len(bucket) == 0 {
|
||||||
|
bucket = a.addrNew[a.rand.Intn(len(a.addrNew))]
|
||||||
|
}
|
||||||
|
// pick a random ka from bucket.
|
||||||
|
randIndex := a.rand.Intn(len(bucket))
|
||||||
|
for _, ka := range bucket {
|
||||||
|
randIndex--
|
||||||
|
if randIndex == 0 {
|
||||||
|
return ka
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("Should not happen")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddrManager) MarkGood(ka *KnownAddress) {
|
func (a *AddrManager) MarkGood(ka *KnownAddress) {
|
||||||
|
a.mtx.Lock(); defer a.mtx.Unlock()
|
||||||
ka.MarkAttempt(true)
|
ka.MarkAttempt(true)
|
||||||
a.moveToOld(ka)
|
a.moveToOld(ka)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AddrManager) MarkBad(ka *KnownAddress) {
|
||||||
|
a.mtx.Lock(); defer a.mtx.Unlock()
|
||||||
|
ka.MarkAttempt(false)
|
||||||
|
}
|
||||||
|
|
||||||
/* Loading & Saving */
|
/* Loading & Saving */
|
||||||
|
|
||||||
func (a *AddrManager) loadPeers() {
|
type addrManagerJSON struct {
|
||||||
|
Key [32]byte
|
||||||
|
AddrNew [newBucketCount]map[string]*KnownAddress
|
||||||
|
AddrOld [oldBucketCount][]*KnownAddress
|
||||||
|
NOld int
|
||||||
|
NNew int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddrManager) savePeers() {
|
func (a *AddrManager) saveToFile(filePath string) {
|
||||||
|
aJSON := &addrManagerJSON{
|
||||||
|
Key: a.key,
|
||||||
|
AddrNew: a.addrNew,
|
||||||
|
AddrOld: a.addrOld,
|
||||||
|
NOld: a.nOld,
|
||||||
|
NNew: a.nNew,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
amgrLog.Error("Error opening file: ", filePath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
defer w.Close()
|
||||||
|
err = enc.Encode(&aJSON)
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AddrManager) loadFromFile(filePath string) {
|
||||||
|
// If doesn't exist, do nothing.
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
if os.IsNotExist(err) { return }
|
||||||
|
|
||||||
|
r, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("%s error opening file: %v", filePath, err))
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
aJSON := &addrManagerJSON{}
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
err = dec.Decode(aJSON)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error reading %s: %v", filePath, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we need to initialize 'a'.
|
||||||
|
|
||||||
|
copy(a.key[:], aJSON.Key[:])
|
||||||
|
a.addrNew = aJSON.AddrNew
|
||||||
|
for i, oldBucket := range aJSON.AddrOld {
|
||||||
|
copy(a.addrOld[i], oldBucket)
|
||||||
|
}
|
||||||
|
a.nNew = aJSON.NNew
|
||||||
|
a.nOld = aJSON.NOld
|
||||||
|
|
||||||
|
a.addrIndex = make(map[string]*KnownAddress)
|
||||||
|
for _, newBucket := range a.addrNew {
|
||||||
|
for key, ka := range newBucket {
|
||||||
|
a.addrIndex[key] = ka
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Private methods */
|
/* Private methods */
|
||||||
|
|
||||||
func (a *AddrManager) addressHandler() {
|
func (a *AddrManager) addressHandler() {
|
||||||
@ -176,13 +268,13 @@ out:
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-dumpAddressTicker.C:
|
case <-dumpAddressTicker.C:
|
||||||
a.savePeers()
|
a.saveToFile(a.filePath)
|
||||||
case <-a.quit:
|
case <-a.quit:
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dumpAddressTicker.Stop()
|
dumpAddressTicker.Stop()
|
||||||
a.savePeers()
|
a.saveToFile(a.filePath)
|
||||||
a.wg.Done()
|
a.wg.Done()
|
||||||
amgrLog.Trace("Address handler done")
|
amgrLog.Trace("Address handler done")
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package peer
|
|||||||
import (
|
import (
|
||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
"time"
|
"time"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -31,6 +32,29 @@ func NewKnownAddress(addr *NetAddress, src *NetAddress) *KnownAddress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadKnownAddress(r io.Reader) *KnownAddress {
|
||||||
|
return &KnownAddress{
|
||||||
|
Addr: ReadNetAddress(r),
|
||||||
|
Src: ReadNetAddress(r),
|
||||||
|
Attempts: ReadUInt32(r),
|
||||||
|
LastAttempt: ReadUInt64(r),
|
||||||
|
LastSuccess: ReadUInt64(r),
|
||||||
|
NewRefs: ReadUInt16(r),
|
||||||
|
OldBucket: ReadInt16(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ka *KnownAddress) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
n, err = WriteOnto(ka.Addr, w, n, err)
|
||||||
|
n, err = WriteOnto(ka.Src, w, n, err)
|
||||||
|
n, err = WriteOnto(ka.Attempts, w, n, err)
|
||||||
|
n, err = WriteOnto(ka.LastAttempt, w, n, err)
|
||||||
|
n, err = WriteOnto(ka.LastSuccess, w, n, err)
|
||||||
|
n, err = WriteOnto(ka.NewRefs, w, n, err)
|
||||||
|
n, err = WriteOnto(ka.OldBucket, w, n, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (ka *KnownAddress) MarkAttempt(success bool) {
|
func (ka *KnownAddress) MarkAttempt(success bool) {
|
||||||
now := UInt64(time.Now().Unix())
|
now := UInt64(time.Now().Unix())
|
||||||
ka.LastAttempt = now
|
ka.LastAttempt = now
|
||||||
|
Loading…
x
Reference in New Issue
Block a user