mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-26 23:32:15 +00:00
p2p: Connect to peers from a seed node immediately (#2115)
This is to reduce wait times when initially connecting. This still runs checks such as whether you still want additional peers. A test case has been created, which fails if this change is not included.
This commit is contained in:
parent
08ad162daa
commit
6fb2f44cc3
@ -20,6 +20,7 @@ IMPROVEMENTS:
|
|||||||
- only process one block at a time to avoid starving
|
- only process one block at a time to avoid starving
|
||||||
- [crypto] Switch hkdfchachapoly1305 to xchachapoly1305
|
- [crypto] Switch hkdfchachapoly1305 to xchachapoly1305
|
||||||
- [common] bit array functions which take in another parameter are now thread safe
|
- [common] bit array functions which take in another parameter are now thread safe
|
||||||
|
- [p2p] begin connecting to peers as soon a seed node provides them to you ([#2093](https://github.com/tendermint/tendermint/issues/2093))
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
- [common] Safely handle cases where atomic write files already exist [#2109](https://github.com/tendermint/tendermint/issues/2109)
|
- [common] Safely handle cases where atomic write files already exist [#2109](https://github.com/tendermint/tendermint/issues/2109)
|
||||||
|
@ -74,6 +74,8 @@ type PEXReactor struct {
|
|||||||
requestsSent *cmn.CMap // ID->struct{}: unanswered send requests
|
requestsSent *cmn.CMap // ID->struct{}: unanswered send requests
|
||||||
lastReceivedRequests *cmn.CMap // ID->time.Time: last time peer requested from us
|
lastReceivedRequests *cmn.CMap // ID->time.Time: last time peer requested from us
|
||||||
|
|
||||||
|
seedAddrs []*p2p.NetAddress
|
||||||
|
|
||||||
attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)}
|
attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,10 +122,13 @@ func (r *PEXReactor) OnStart() error {
|
|||||||
|
|
||||||
// return err if user provided a bad seed address
|
// return err if user provided a bad seed address
|
||||||
// or a host name that we cant resolve
|
// or a host name that we cant resolve
|
||||||
if err := r.checkSeeds(); err != nil {
|
seedAddrs, err := r.checkSeeds()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.seedAddrs = seedAddrs
|
||||||
|
|
||||||
// Check if this node should run
|
// Check if this node should run
|
||||||
// in seed/crawler mode
|
// in seed/crawler mode
|
||||||
if r.config.SeedMode {
|
if r.config.SeedMode {
|
||||||
@ -281,7 +286,6 @@ func (r *PEXReactor) RequestAddrs(p Peer) {
|
|||||||
// request for this peer and deletes the open request.
|
// request for this peer and deletes the open request.
|
||||||
// If there's no open request for the src peer, it returns an error.
|
// If there's no open request for the src peer, it returns an error.
|
||||||
func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error {
|
func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error {
|
||||||
|
|
||||||
id := string(src.ID())
|
id := string(src.ID())
|
||||||
if !r.requestsSent.Has(id) {
|
if !r.requestsSent.Has(id) {
|
||||||
return cmn.NewError("Received unsolicited pexAddrsMessage")
|
return cmn.NewError("Received unsolicited pexAddrsMessage")
|
||||||
@ -297,6 +301,13 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error {
|
|||||||
|
|
||||||
err := r.book.AddAddress(netAddr, srcAddr)
|
err := r.book.AddAddress(netAddr, srcAddr)
|
||||||
r.logErrAddrBook(err)
|
r.logErrAddrBook(err)
|
||||||
|
|
||||||
|
// If this address came from a seed node, try to connect to it without waiting.
|
||||||
|
for _, seedAddr := range r.seedAddrs {
|
||||||
|
if seedAddr.Equals(srcAddr) {
|
||||||
|
r.ensurePeers()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -468,18 +479,18 @@ func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check seed addresses are well formed
|
// check seed addresses are well formed
|
||||||
func (r *PEXReactor) checkSeeds() error {
|
func (r *PEXReactor) checkSeeds() ([]*p2p.NetAddress, error) {
|
||||||
lSeeds := len(r.config.Seeds)
|
lSeeds := len(r.config.Seeds)
|
||||||
if lSeeds == 0 {
|
if lSeeds == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
_, errs := p2p.NewNetAddressStrings(r.config.Seeds)
|
netAddrs, errs := p2p.NewNetAddressStrings(r.config.Seeds)
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return netAddrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// randomly dial seeds until we connect to one or exhaust them
|
// randomly dial seeds until we connect to one or exhaust them
|
||||||
@ -488,13 +499,12 @@ func (r *PEXReactor) dialSeeds() {
|
|||||||
if lSeeds == 0 {
|
if lSeeds == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
seedAddrs, _ := p2p.NewNetAddressStrings(r.config.Seeds)
|
|
||||||
|
|
||||||
perm := cmn.RandPerm(lSeeds)
|
perm := cmn.RandPerm(lSeeds)
|
||||||
// perm := r.Switch.rng.Perm(lSeeds)
|
// perm := r.Switch.rng.Perm(lSeeds)
|
||||||
for _, i := range perm {
|
for _, i := range perm {
|
||||||
// dial a random seed
|
// dial a random seed
|
||||||
seedAddr := seedAddrs[i]
|
seedAddr := r.seedAddrs[i]
|
||||||
err := r.Switch.DialPeerWithAddress(seedAddr, false)
|
err := r.Switch.DialPeerWithAddress(seedAddr, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
|
@ -211,31 +211,26 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) {
|
|||||||
defer os.RemoveAll(dir) // nolint: errcheck
|
defer os.RemoveAll(dir) // nolint: errcheck
|
||||||
|
|
||||||
// 1. create seed
|
// 1. create seed
|
||||||
seed := p2p.MakeSwitch(
|
seed := testCreateSeed(dir, 0, []*p2p.NetAddress{}, []*p2p.NetAddress{})
|
||||||
cfg,
|
|
||||||
0,
|
|
||||||
"127.0.0.1",
|
|
||||||
"123.123.123",
|
|
||||||
func(i int, sw *p2p.Switch) *p2p.Switch {
|
|
||||||
book := NewAddrBook(filepath.Join(dir, "addrbook0.json"), false)
|
|
||||||
book.SetLogger(log.TestingLogger())
|
|
||||||
sw.SetAddrBook(book)
|
|
||||||
|
|
||||||
sw.SetLogger(log.TestingLogger())
|
|
||||||
|
|
||||||
r := NewPEXReactor(book, &PEXReactorConfig{})
|
|
||||||
r.SetLogger(log.TestingLogger())
|
|
||||||
sw.AddReactor("pex", r)
|
|
||||||
return sw
|
|
||||||
},
|
|
||||||
)
|
|
||||||
seed.AddListener(
|
|
||||||
p2p.NewDefaultListener("tcp://"+seed.NodeInfo().ListenAddr, "", false, log.TestingLogger()),
|
|
||||||
)
|
|
||||||
require.Nil(t, seed.Start())
|
require.Nil(t, seed.Start())
|
||||||
defer seed.Stop()
|
defer seed.Stop()
|
||||||
|
|
||||||
// 2. create usual peer with only seed configured.
|
// 2. create usual peer with only seed configured.
|
||||||
|
peer := testCreatePeerWithSeed(dir, 1, seed)
|
||||||
|
require.Nil(t, peer.Start())
|
||||||
|
defer peer.Stop()
|
||||||
|
|
||||||
|
// 3. check that the peer connects to seed immediately
|
||||||
|
assertPeersWithTimeout(t, []*p2p.Switch{peer}, 10*time.Millisecond, 3*time.Second, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnectionSpeedForPeerReceivedFromSeed(t *testing.T) {
|
||||||
|
// directory to store address books
|
||||||
|
dir, err := ioutil.TempDir("", "pex_reactor")
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer os.RemoveAll(dir) // nolint: errcheck
|
||||||
|
|
||||||
|
// 1. create peer
|
||||||
peer := p2p.MakeSwitch(
|
peer := p2p.MakeSwitch(
|
||||||
cfg,
|
cfg,
|
||||||
1,
|
1,
|
||||||
@ -250,20 +245,34 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) {
|
|||||||
|
|
||||||
r := NewPEXReactor(
|
r := NewPEXReactor(
|
||||||
book,
|
book,
|
||||||
&PEXReactorConfig{
|
&PEXReactorConfig{},
|
||||||
Seeds: []string{seed.NodeInfo().NetAddress().String()},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
r.SetLogger(log.TestingLogger())
|
r.SetLogger(log.TestingLogger())
|
||||||
sw.AddReactor("pex", r)
|
sw.AddReactor("pex", r)
|
||||||
return sw
|
return sw
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
peer.AddListener(
|
||||||
|
p2p.NewDefaultListener("tcp://"+peer.NodeInfo().ListenAddr, "", false, log.TestingLogger()),
|
||||||
|
)
|
||||||
require.Nil(t, peer.Start())
|
require.Nil(t, peer.Start())
|
||||||
defer peer.Stop()
|
defer peer.Stop()
|
||||||
|
|
||||||
// 3. check that the peer connects to seed immediately
|
// 2. Create seed which knows about the peer
|
||||||
assertPeersWithTimeout(t, []*p2p.Switch{peer}, 10*time.Millisecond, 3*time.Second, 1)
|
seed := testCreateSeed(dir, 2, []*p2p.NetAddress{peer.NodeInfo().NetAddress()}, []*p2p.NetAddress{peer.NodeInfo().NetAddress()})
|
||||||
|
require.Nil(t, seed.Start())
|
||||||
|
defer seed.Stop()
|
||||||
|
|
||||||
|
// 3. create another peer with only seed configured.
|
||||||
|
secondPeer := testCreatePeerWithSeed(dir, 3, seed)
|
||||||
|
require.Nil(t, secondPeer.Start())
|
||||||
|
defer secondPeer.Stop()
|
||||||
|
|
||||||
|
// 4. check that the second peer connects to seed immediately
|
||||||
|
assertPeersWithTimeout(t, []*p2p.Switch{secondPeer}, 10*time.Millisecond, 3*time.Second, 1)
|
||||||
|
|
||||||
|
// 5. check that the second peer connects to the first peer immediately
|
||||||
|
assertPeersWithTimeout(t, []*p2p.Switch{secondPeer}, 10*time.Millisecond, 1*time.Second, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPEXReactorCrawlStatus(t *testing.T) {
|
func TestPEXReactorCrawlStatus(t *testing.T) {
|
||||||
@ -401,6 +410,7 @@ func assertPeersWithTimeout(
|
|||||||
outbound, inbound, _ := s.NumPeers()
|
outbound, inbound, _ := s.NumPeers()
|
||||||
if outbound+inbound < nPeers {
|
if outbound+inbound < nPeers {
|
||||||
allGood = false
|
allGood = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remaining -= checkPeriod
|
remaining -= checkPeriod
|
||||||
@ -417,14 +427,77 @@ func assertPeersWithTimeout(
|
|||||||
numPeersStr += fmt.Sprintf("%d => {outbound: %d, inbound: %d}, ", i, outbound, inbound)
|
numPeersStr += fmt.Sprintf("%d => {outbound: %d, inbound: %d}, ", i, outbound, inbound)
|
||||||
}
|
}
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"expected all switches to be connected to at least one peer (switches: %s)",
|
"expected all switches to be connected to at least %d peer(s) (switches: %s)",
|
||||||
numPeersStr,
|
nPeers, numPeersStr,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a seed which knows about the provided addresses / source address pairs.
|
||||||
|
// Starting and stopping the seed is left to the caller
|
||||||
|
func testCreateSeed(dir string, id int, knownAddrs, srcAddrs []*p2p.NetAddress) *p2p.Switch {
|
||||||
|
seed := p2p.MakeSwitch(
|
||||||
|
cfg,
|
||||||
|
id,
|
||||||
|
"127.0.0.1",
|
||||||
|
"123.123.123",
|
||||||
|
func(i int, sw *p2p.Switch) *p2p.Switch {
|
||||||
|
book := NewAddrBook(filepath.Join(dir, "addrbookSeed.json"), false)
|
||||||
|
book.SetLogger(log.TestingLogger())
|
||||||
|
for j := 0; j < len(knownAddrs); j++ {
|
||||||
|
book.AddAddress(knownAddrs[j], srcAddrs[j])
|
||||||
|
book.MarkGood(knownAddrs[j])
|
||||||
|
}
|
||||||
|
sw.SetAddrBook(book)
|
||||||
|
|
||||||
|
sw.SetLogger(log.TestingLogger())
|
||||||
|
|
||||||
|
r := NewPEXReactor(book, &PEXReactorConfig{})
|
||||||
|
r.SetLogger(log.TestingLogger())
|
||||||
|
sw.AddReactor("pex", r)
|
||||||
|
return sw
|
||||||
|
},
|
||||||
|
)
|
||||||
|
seed.AddListener(
|
||||||
|
p2p.NewDefaultListener("tcp://"+seed.NodeInfo().ListenAddr, "", false, log.TestingLogger()),
|
||||||
|
)
|
||||||
|
return seed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a peer which knows about the provided seed.
|
||||||
|
// Starting and stopping the peer is left to the caller
|
||||||
|
func testCreatePeerWithSeed(dir string, id int, seed *p2p.Switch) *p2p.Switch {
|
||||||
|
peer := p2p.MakeSwitch(
|
||||||
|
cfg,
|
||||||
|
id,
|
||||||
|
"127.0.0.1",
|
||||||
|
"123.123.123",
|
||||||
|
func(i int, sw *p2p.Switch) *p2p.Switch {
|
||||||
|
book := NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", id)), false)
|
||||||
|
book.SetLogger(log.TestingLogger())
|
||||||
|
sw.SetAddrBook(book)
|
||||||
|
|
||||||
|
sw.SetLogger(log.TestingLogger())
|
||||||
|
|
||||||
|
r := NewPEXReactor(
|
||||||
|
book,
|
||||||
|
&PEXReactorConfig{
|
||||||
|
Seeds: []string{seed.NodeInfo().NetAddress().String()},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
r.SetLogger(log.TestingLogger())
|
||||||
|
sw.AddReactor("pex", r)
|
||||||
|
return sw
|
||||||
|
},
|
||||||
|
)
|
||||||
|
peer.AddListener(
|
||||||
|
p2p.NewDefaultListener("tcp://"+peer.NodeInfo().ListenAddr, "", false, log.TestingLogger()),
|
||||||
|
)
|
||||||
|
return peer
|
||||||
|
}
|
||||||
|
|
||||||
func createReactor(conf *PEXReactorConfig) (r *PEXReactor, book *addrBook) {
|
func createReactor(conf *PEXReactorConfig) (r *PEXReactor, book *addrBook) {
|
||||||
// directory to store address book
|
// directory to store address book
|
||||||
dir, err := ioutil.TempDir("", "pex_reactor")
|
dir, err := ioutil.TempDir("", "pex_reactor")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user