mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-09 01:26:19 +00:00
p2p: tighten up and test PeerManager (#6034)
This tightens up the `PeerManager` and related code, adds a ton of tests, and fixes a bunch of inconsistencies and bugs.
This commit is contained in:
+1
-2
@@ -106,10 +106,9 @@ func ParseNodeAddress(urlString string) (NodeAddress, error) {
|
||||
Protocol: Protocol(strings.ToLower(url.Scheme)),
|
||||
}
|
||||
|
||||
// Opaque URLs are expected to contain only a node ID, also used as path.
|
||||
// Opaque URLs are expected to contain only a node ID.
|
||||
if url.Opaque != "" {
|
||||
address.NodeID = NodeID(url.Opaque)
|
||||
address.Path = url.Opaque
|
||||
return address, address.Validate()
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ func TestParseNodeAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"memory:" + user,
|
||||
p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: user},
|
||||
p2p.NodeAddress{Protocol: "memory", NodeID: id},
|
||||
true,
|
||||
},
|
||||
|
||||
|
||||
@@ -27,6 +27,21 @@ func (e Envelope) Strip() Envelope {
|
||||
return e
|
||||
}
|
||||
|
||||
// PeerError is a peer error reported via the Error channel.
|
||||
//
|
||||
// FIXME: This currently just disconnects the peer, which is too simplistic.
|
||||
// For example, some errors should be logged, some should cause disconnects,
|
||||
// and some should ban the peer.
|
||||
//
|
||||
// FIXME: This should probably be replaced by a more general PeerBehavior
|
||||
// concept that can mark good and bad behavior and contributes to peer scoring.
|
||||
// It should possibly also allow reactors to request explicit actions, e.g.
|
||||
// disconnection or banning, in addition to doing this based on aggregates.
|
||||
type PeerError struct {
|
||||
NodeID NodeID
|
||||
Err error
|
||||
}
|
||||
|
||||
// Channel is a bidirectional channel for Protobuf message exchange with peers.
|
||||
// A Channel is safe for concurrent use by multiple goroutines.
|
||||
type Channel struct {
|
||||
|
||||
-1157
File diff suppressed because it is too large
Load Diff
+1275
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+6
-7
@@ -30,7 +30,7 @@ type ReactorV2 struct {
|
||||
|
||||
peerManager *p2p.PeerManager
|
||||
pexCh *p2p.Channel
|
||||
peerUpdates *p2p.PeerUpdatesCh
|
||||
peerUpdates *p2p.PeerUpdates
|
||||
closeCh chan struct{}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func NewReactorV2(
|
||||
logger log.Logger,
|
||||
peerManager *p2p.PeerManager,
|
||||
pexCh *p2p.Channel,
|
||||
peerUpdates *p2p.PeerUpdatesCh,
|
||||
peerUpdates *p2p.PeerUpdates,
|
||||
) *ReactorV2 {
|
||||
r := &ReactorV2{
|
||||
peerManager: peerManager,
|
||||
@@ -181,9 +181,8 @@ func (r *ReactorV2) processPexCh() {
|
||||
if err := r.handleMessage(r.pexCh.ID(), envelope); err != nil {
|
||||
r.Logger.Error("failed to process message", "ch_id", r.pexCh.ID(), "envelope", envelope, "err", err)
|
||||
r.pexCh.Error() <- p2p.PeerError{
|
||||
PeerID: envelope.From,
|
||||
Err: err,
|
||||
Severity: p2p.PeerErrorSeverityLow,
|
||||
NodeID: envelope.From,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,11 +196,11 @@ func (r *ReactorV2) processPexCh() {
|
||||
// processPeerUpdate processes a PeerUpdate. For added peers, PeerStatusUp, we
|
||||
// send a request for addresses.
|
||||
func (r *ReactorV2) processPeerUpdate(peerUpdate p2p.PeerUpdate) {
|
||||
r.Logger.Debug("received peer update", "peer", peerUpdate.PeerID, "status", peerUpdate.Status)
|
||||
r.Logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
|
||||
|
||||
if peerUpdate.Status == p2p.PeerStatusUp {
|
||||
r.pexCh.Out() <- p2p.Envelope{
|
||||
To: peerUpdate.PeerID,
|
||||
To: peerUpdate.NodeID,
|
||||
Message: &protop2p.PexRequest{},
|
||||
}
|
||||
}
|
||||
|
||||
+19
-13
@@ -255,13 +255,10 @@ func (r *Router) routeChannel(channel *Channel) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// FIXME: We just disconnect the peer for now
|
||||
r.logger.Error("peer error, disconnecting", "peer", peerError.PeerID, "err", peerError.Err)
|
||||
r.peerMtx.RLock()
|
||||
peerQueue, ok := r.peerQueues[peerError.PeerID]
|
||||
r.peerMtx.RUnlock()
|
||||
if ok {
|
||||
peerQueue.close()
|
||||
// FIXME: We just evict the peer for now.
|
||||
r.logger.Error("peer error, evicting", "peer", peerError.NodeID, "err", peerError.Err)
|
||||
if err := r.peerManager.Errored(peerError.NodeID, peerError.Err); err != nil {
|
||||
r.logger.Error("failed to report peer error", "peer", peerError.NodeID, "err", err)
|
||||
}
|
||||
|
||||
case <-channel.Done():
|
||||
@@ -338,7 +335,6 @@ func (r *Router) acceptPeers(transport Transport) {
|
||||
r.peerMtx.Lock()
|
||||
r.peerQueues[peerInfo.NodeID] = queue
|
||||
r.peerMtx.Unlock()
|
||||
r.peerManager.Ready(peerInfo.NodeID)
|
||||
|
||||
defer func() {
|
||||
r.peerMtx.Lock()
|
||||
@@ -350,6 +346,11 @@ func (r *Router) acceptPeers(transport Transport) {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := r.peerManager.Ready(peerInfo.NodeID); err != nil {
|
||||
r.logger.Error("failed to mark peer as ready", "peer", peerInfo.NodeID, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
r.routePeer(peerInfo.NodeID, conn, queue)
|
||||
}()
|
||||
}
|
||||
@@ -359,7 +360,7 @@ func (r *Router) acceptPeers(transport Transport) {
|
||||
func (r *Router) dialPeers() {
|
||||
ctx := r.stopCtx()
|
||||
for {
|
||||
peerID, address, err := r.peerManager.DialNext(ctx)
|
||||
address, err := r.peerManager.DialNext(ctx)
|
||||
switch err {
|
||||
case nil:
|
||||
case context.Canceled:
|
||||
@@ -371,12 +372,13 @@ func (r *Router) dialPeers() {
|
||||
}
|
||||
|
||||
go func() {
|
||||
peerID := address.NodeID
|
||||
conn, err := r.dialPeer(ctx, address)
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
} else if err != nil {
|
||||
r.logger.Error("failed to dial peer", "peer", peerID, "err", err)
|
||||
if err = r.peerManager.DialFailed(peerID, address); err != nil {
|
||||
if err = r.peerManager.DialFailed(address); err != nil {
|
||||
r.logger.Error("failed to report dial failure", "peer", peerID, "err", err)
|
||||
}
|
||||
return
|
||||
@@ -388,13 +390,13 @@ func (r *Router) dialPeers() {
|
||||
return
|
||||
} else if err != nil {
|
||||
r.logger.Error("failed to handshake with peer", "peer", peerID, "err", err)
|
||||
if err = r.peerManager.DialFailed(peerID, address); err != nil {
|
||||
if err = r.peerManager.DialFailed(address); err != nil {
|
||||
r.logger.Error("failed to report dial failure", "peer", peerID, "err", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.peerManager.Dialed(peerID, address); err != nil {
|
||||
if err = r.peerManager.Dialed(address); err != nil {
|
||||
r.logger.Error("failed to dial peer", "peer", peerID, "err", err)
|
||||
return
|
||||
}
|
||||
@@ -403,7 +405,6 @@ func (r *Router) dialPeers() {
|
||||
r.peerMtx.Lock()
|
||||
r.peerQueues[peerID] = queue
|
||||
r.peerMtx.Unlock()
|
||||
r.peerManager.Ready(peerID)
|
||||
|
||||
defer func() {
|
||||
r.peerMtx.Lock()
|
||||
@@ -415,6 +416,11 @@ func (r *Router) dialPeers() {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := r.peerManager.Ready(peerID); err != nil {
|
||||
r.logger.Error("failed to mark peer as ready", "peer", peerID, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
r.routePeer(peerID, conn, queue)
|
||||
}()
|
||||
}
|
||||
|
||||
+6
-7
@@ -115,9 +115,9 @@ func TestRouter(t *testing.T) {
|
||||
// Wait for peers to come online, and ping them as they do.
|
||||
for i := 0; i < len(peers); i++ {
|
||||
peerUpdate := <-peerUpdates.Updates()
|
||||
peerID := peerUpdate.PeerID
|
||||
peerID := peerUpdate.NodeID
|
||||
require.Equal(t, p2p.PeerUpdate{
|
||||
PeerID: peerID,
|
||||
NodeID: peerID,
|
||||
Status: p2p.PeerStatusUp,
|
||||
}, peerUpdate)
|
||||
|
||||
@@ -140,13 +140,12 @@ func TestRouter(t *testing.T) {
|
||||
|
||||
// We then submit an error for a peer, and watch it get disconnected.
|
||||
channel.Error() <- p2p.PeerError{
|
||||
PeerID: peers[0].NodeID,
|
||||
Err: errors.New("test error"),
|
||||
Severity: p2p.PeerErrorSeverityCritical,
|
||||
NodeID: peers[0].NodeID,
|
||||
Err: errors.New("test error"),
|
||||
}
|
||||
peerUpdate := <-peerUpdates.Updates()
|
||||
require.Equal(t, p2p.PeerUpdate{
|
||||
PeerID: peers[0].NodeID,
|
||||
NodeID: peers[0].NodeID,
|
||||
Status: p2p.PeerStatusDown,
|
||||
}, peerUpdate)
|
||||
|
||||
@@ -154,7 +153,7 @@ func TestRouter(t *testing.T) {
|
||||
// for that to happen.
|
||||
peerUpdate = <-peerUpdates.Updates()
|
||||
require.Equal(t, p2p.PeerUpdate{
|
||||
PeerID: peers[0].NodeID,
|
||||
NodeID: peers[0].NodeID,
|
||||
Status: p2p.PeerStatusUp,
|
||||
}, peerUpdate)
|
||||
}
|
||||
|
||||
+6
-6
@@ -29,7 +29,7 @@ type (
|
||||
BaseReactor
|
||||
|
||||
Name string
|
||||
PeerUpdates *PeerUpdatesCh
|
||||
PeerUpdates *PeerUpdates
|
||||
Channels map[ChannelID]*ChannelShim
|
||||
}
|
||||
|
||||
@@ -162,10 +162,10 @@ func (rs *ReactorShim) handlePeerErrors() {
|
||||
for _, cs := range rs.Channels {
|
||||
go func(cs *ChannelShim) {
|
||||
for pErr := range cs.Channel.errCh {
|
||||
if pErr.PeerID != "" {
|
||||
peer := rs.Switch.peers.Get(pErr.PeerID)
|
||||
if pErr.NodeID != "" {
|
||||
peer := rs.Switch.peers.Get(pErr.NodeID)
|
||||
if peer == nil {
|
||||
rs.Logger.Error("failed to handle peer error; failed to find peer", "peer", pErr.PeerID)
|
||||
rs.Logger.Error("failed to handle peer error; failed to find peer", "peer", pErr.NodeID)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ func (rs *ReactorShim) GetChannels() []*ChannelDescriptor {
|
||||
// handle adding a peer.
|
||||
func (rs *ReactorShim) AddPeer(peer Peer) {
|
||||
select {
|
||||
case rs.PeerUpdates.updatesCh <- PeerUpdate{PeerID: peer.ID(), Status: PeerStatusUp}:
|
||||
case rs.PeerUpdates.updatesCh <- PeerUpdate{NodeID: peer.ID(), Status: PeerStatusUp}:
|
||||
rs.Logger.Debug("sent peer update", "reactor", rs.Name, "peer", peer.ID(), "status", PeerStatusUp)
|
||||
|
||||
case <-rs.PeerUpdates.Done():
|
||||
@@ -244,7 +244,7 @@ func (rs *ReactorShim) AddPeer(peer Peer) {
|
||||
// handle removing a peer.
|
||||
func (rs *ReactorShim) RemovePeer(peer Peer, reason interface{}) {
|
||||
select {
|
||||
case rs.PeerUpdates.updatesCh <- PeerUpdate{PeerID: peer.ID(), Status: PeerStatusDown}:
|
||||
case rs.PeerUpdates.updatesCh <- PeerUpdate{NodeID: peer.ID(), Status: PeerStatusDown}:
|
||||
rs.Logger.Debug(
|
||||
"sent peer update",
|
||||
"reactor", rs.Name,
|
||||
|
||||
+2
-2
@@ -123,7 +123,7 @@ func TestReactorShim_AddPeer(t *testing.T) {
|
||||
rts.shim.AddPeer(peerA)
|
||||
wg.Wait()
|
||||
|
||||
require.Equal(t, peerIDA, peerUpdate.PeerID)
|
||||
require.Equal(t, peerIDA, peerUpdate.NodeID)
|
||||
require.Equal(t, p2p.PeerStatusUp, peerUpdate.Status)
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func TestReactorShim_RemovePeer(t *testing.T) {
|
||||
rts.shim.RemovePeer(peerA, "test reason")
|
||||
wg.Wait()
|
||||
|
||||
require.Equal(t, peerIDA, peerUpdate.PeerID)
|
||||
require.Equal(t, peerIDA, peerUpdate.NodeID)
|
||||
require.Equal(t, p2p.PeerStatusDown, peerUpdate.Status)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user