github.com/aergoio/aergo@v1.3.1/p2p/raftsupport/clusterreceiver.go (about)

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package raftsupport
     7  
     8  import (
     9  	"fmt"
    10  	"github.com/pkg/errors"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/aergoio/aergo/message"
    15  	"github.com/aergoio/aergo/p2p/p2pcommon"
    16  	"github.com/aergoio/aergo/types"
    17  	"github.com/golang/protobuf/proto"
    18  )
    19  
    20  // ClusterInfoReceiver is send p2p getClusterInfo to connected peers and Receive p2p responses one of peers return successful response
    21  // The first version will be simplified version. it send and Receive one by one.
    22  type ClusterInfoReceiver struct {
    23  	mf p2pcommon.MoFactory
    24  
    25  	peers  []p2pcommon.RemotePeer
    26  	mutex  sync.Mutex
    27  	sents  map[p2pcommon.MsgID]p2pcommon.RemotePeer
    28  	offset int
    29  
    30  	req   *message.GetCluster
    31  	actor p2pcommon.ActorService
    32  
    33  	ttl      time.Duration
    34  	timeout  time.Time
    35  	finished bool
    36  	status   receiverStatus
    37  
    38  	got            []*types.Block
    39  	senderFinished chan interface{}
    40  }
    41  
    42  type receiverStatus int32
    43  
    44  const (
    45  	receiverStatusWaiting receiverStatus = iota
    46  	receiverStatusCanceled
    47  	receiverStatusFinished
    48  )
    49  
    50  func NewClusterInfoReceiver(actor p2pcommon.ActorService, mf p2pcommon.MoFactory, peers []p2pcommon.RemotePeer, ttl time.Duration, req *message.GetCluster) *ClusterInfoReceiver {
    51  	return &ClusterInfoReceiver{actor: actor, mf: mf, peers: peers, ttl: ttl, req: req, sents: make(map[p2pcommon.MsgID]p2pcommon.RemotePeer)}
    52  }
    53  
    54  func (br *ClusterInfoReceiver) StartGet() {
    55  	br.timeout = time.Now().Add(br.ttl)
    56  	// create message data
    57  	// send message to first peer
    58  	go func() {
    59  		br.mutex.Lock()
    60  		defer br.mutex.Unlock()
    61  		if !br.trySendNextPeer() {
    62  			br.cancelReceiving(errors.New("no live peers"), false)
    63  		}
    64  	}()
    65  }
    66  
    67  func (br *ClusterInfoReceiver) trySendNextPeer() bool {
    68  	for ; br.offset < len(br.peers); br.offset++ {
    69  		peer := br.peers[br.offset]
    70  		if peer.State() == types.RUNNING {
    71  			br.offset++
    72  			mo := br.mf.NewMsgBlockRequestOrder(br.ReceiveResp, p2pcommon.GetClusterRequest, &types.GetClusterInfoRequest{BestBlockHash: br.req.BestBlockHash})
    73  			peer.SendMessage(mo)
    74  			br.sents[mo.GetMsgID()] = peer
    75  			return true
    76  		}
    77  	}
    78  	return false
    79  }
    80  
    81  // ReceiveResp must be called just in read go routine
    82  func (br *ClusterInfoReceiver) ReceiveResp(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) (ret bool) {
    83  	// cases in waiting
    84  	//   normal not status => wait
    85  	//   normal status (last response)  => finish
    86  	//   abnormal resp (no following resp expected): hasNext is true => cancel
    87  	//   abnormal resp (following resp expected): hasNext is false, or invalid resp data type (maybe remote peer is totally broken) => cancel finish
    88  	// case in status or status
    89  	ret = true
    90  	br.mutex.Lock()
    91  	defer br.mutex.Unlock()
    92  	// consuming request id at first
    93  	peer, exist := br.sents[msg.OriginalID()]
    94  	if exist {
    95  		delete(br.sents, msg.OriginalID())
    96  		peer.ConsumeRequest(msg.OriginalID())
    97  	}
    98  
    99  	status := br.status
   100  	switch status {
   101  	case receiverStatusWaiting:
   102  		br.handleInWaiting(msg, msgBody)
   103  	case receiverStatusCanceled:
   104  		fallthrough
   105  	case receiverStatusFinished:
   106  		fallthrough
   107  	default:
   108  		br.ignoreMsg(msg, msgBody)
   109  		return
   110  	}
   111  	return
   112  }
   113  
   114  func (br *ClusterInfoReceiver) handleInWaiting(msg p2pcommon.Message, msgBody proto.Message) {
   115  	// timeout
   116  	if br.timeout.Before(time.Now()) {
   117  		// silently ignore already finished job
   118  		br.finishReceiver()
   119  		return
   120  	}
   121  
   122  	// remote peer response malformed data.
   123  	body, ok := msgBody.(*types.GetClusterInfoResponse)
   124  	if !ok || len(body.MbrAttrs) == 0 || body.Error != "" {
   125  		// TODO log fail reason?
   126  		if !br.trySendNextPeer() {
   127  			br.cancelReceiving(errors.New("no live peers"), false)
   128  		}
   129  		return
   130  	}
   131  
   132  	// return the result
   133  	var err error
   134  	br.finishReceiver()
   135  	if len(body.Error) != 0 {
   136  		err = fmt.Errorf("get cluster info error: %s", body.Error)
   137  	}
   138  	result := &message.GetClusterRsp{ClusterID: body.GetClusterID(), ChainID: body.GetChainID(), Members: body.GetMbrAttrs(),
   139  		Err: err, HardStateInfo: body.HardStateInfo}
   140  	br.req.ReplyC <- result
   141  	close(br.req.ReplyC)
   142  	return
   143  }
   144  
   145  // cancelReceiving is cancel wait for receiving and return the failure result.
   146  // it wait remaining (and useless) response. It is assumed cancellations are not frequently occur
   147  func (br *ClusterInfoReceiver) cancelReceiving(err error, hasNext bool) {
   148  	br.status = receiverStatusCanceled
   149  	result := &message.GetClusterRsp{Err: err}
   150  	br.req.ReplyC <- result
   151  	close(br.req.ReplyC)
   152  	br.finishReceiver()
   153  }
   154  
   155  // finishReceiver is to cancel works, assuming cancellations are not frequently occur
   156  func (br *ClusterInfoReceiver) finishReceiver() {
   157  	br.status = receiverStatusFinished
   158  }
   159  
   160  // ignoreMsg is silently ignore following responses, which is not useless anymore.
   161  func (br *ClusterInfoReceiver) ignoreMsg(msg p2pcommon.Message, msgBody proto.Message) {
   162  	// nothing to do for now
   163  }