github.com/anjalikarhana/fabric@v2.1.1+incompatible/orderer/common/cluster/rpc.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package cluster
     8  
     9  import (
    10  	"context"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/hyperledger/fabric-protos-go/orderer"
    15  	"github.com/hyperledger/fabric/common/flogging"
    16  	"github.com/pkg/errors"
    17  	"go.uber.org/zap/zapcore"
    18  	"google.golang.org/grpc"
    19  )
    20  
    21  //go:generate mockery -dir . -name StepClient -case underscore -output ./mocks/
    22  
    23  // StepClient defines a client that sends and receives Step requests and responses.
    24  type StepClient interface {
    25  	Send(*orderer.StepRequest) error
    26  	Recv() (*orderer.StepResponse, error)
    27  	grpc.ClientStream
    28  }
    29  
    30  //go:generate mockery -dir . -name ClusterClient -case underscore -output ./mocks/
    31  
    32  // ClusterClient creates streams that point to a remote cluster member.
    33  type ClusterClient interface {
    34  	Step(ctx context.Context, opts ...grpc.CallOption) (orderer.Cluster_StepClient, error)
    35  }
    36  
    37  // RPC performs remote procedure calls to remote cluster nodes.
    38  type RPC struct {
    39  	consensusLock sync.Mutex
    40  	submitLock    sync.Mutex
    41  	Logger        *flogging.FabricLogger
    42  	Timeout       time.Duration
    43  	Channel       string
    44  	Comm          Communicator
    45  	lock          sync.RWMutex
    46  	StreamsByType map[OperationType]map[uint64]*Stream
    47  }
    48  
    49  // NewStreamsByType returns a mapping of operation type to
    50  // a mapping of destination to stream.
    51  func NewStreamsByType() map[OperationType]map[uint64]*Stream {
    52  	m := make(map[OperationType]map[uint64]*Stream)
    53  	m[ConsensusOperation] = make(map[uint64]*Stream)
    54  	m[SubmitOperation] = make(map[uint64]*Stream)
    55  	return m
    56  }
    57  
    58  // OperationType denotes a type of operation that the RPC can perform
    59  // such as sending a transaction, or a consensus related message.
    60  type OperationType int
    61  
    62  const (
    63  	ConsensusOperation OperationType = iota
    64  	SubmitOperation
    65  )
    66  
    67  // SendConsensus passes the given ConsensusRequest message to the raft.Node instance.
    68  func (s *RPC) SendConsensus(destination uint64, msg *orderer.ConsensusRequest) error {
    69  	if s.Logger.IsEnabledFor(zapcore.DebugLevel) {
    70  		defer s.consensusSent(time.Now(), destination, msg)
    71  	}
    72  
    73  	stream, err := s.getOrCreateStream(destination, ConsensusOperation)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	req := &orderer.StepRequest{
    79  		Payload: &orderer.StepRequest_ConsensusRequest{
    80  			ConsensusRequest: msg,
    81  		},
    82  	}
    83  
    84  	s.consensusLock.Lock()
    85  	defer s.consensusLock.Unlock()
    86  
    87  	err = stream.Send(req)
    88  	if err != nil {
    89  		s.unMapStream(destination, ConsensusOperation)
    90  	}
    91  
    92  	return err
    93  }
    94  
    95  // SendSubmit sends a SubmitRequest to the given destination node.
    96  func (s *RPC) SendSubmit(destination uint64, request *orderer.SubmitRequest) error {
    97  	if s.Logger.IsEnabledFor(zapcore.DebugLevel) {
    98  		defer s.submitSent(time.Now(), destination, request)
    99  	}
   100  
   101  	stream, err := s.getOrCreateStream(destination, SubmitOperation)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	req := &orderer.StepRequest{
   107  		Payload: &orderer.StepRequest_SubmitRequest{
   108  			SubmitRequest: request,
   109  		},
   110  	}
   111  
   112  	s.submitLock.Lock()
   113  	defer s.submitLock.Unlock()
   114  
   115  	err = stream.Send(req)
   116  	if err != nil {
   117  		s.unMapStream(destination, SubmitOperation)
   118  	}
   119  	return err
   120  }
   121  
   122  func (s *RPC) submitSent(start time.Time, to uint64, msg *orderer.SubmitRequest) {
   123  	s.Logger.Debugf("Sending msg of %d bytes to %d on channel %s took %v", submitMsgLength(msg), to, s.Channel, time.Since(start))
   124  }
   125  
   126  func (s *RPC) consensusSent(start time.Time, to uint64, msg *orderer.ConsensusRequest) {
   127  	s.Logger.Debugf("Sending msg of %d bytes to %d on channel %s took %v", len(msg.Payload), to, s.Channel, time.Since(start))
   128  }
   129  
   130  // getOrCreateStream obtains a Submit stream for the given destination node
   131  func (s *RPC) getOrCreateStream(destination uint64, operationType OperationType) (orderer.Cluster_StepClient, error) {
   132  	stream := s.getStream(destination, operationType)
   133  	if stream != nil {
   134  		return stream, nil
   135  	}
   136  	stub, err := s.Comm.Remote(s.Channel, destination)
   137  	if err != nil {
   138  		return nil, errors.WithStack(err)
   139  	}
   140  	stream, err = stub.NewStream(s.Timeout)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	s.mapStream(destination, stream, operationType)
   145  	return stream, nil
   146  }
   147  
   148  func (s *RPC) getStream(destination uint64, operationType OperationType) *Stream {
   149  	s.lock.RLock()
   150  	defer s.lock.RUnlock()
   151  	return s.StreamsByType[operationType][destination]
   152  }
   153  
   154  func (s *RPC) mapStream(destination uint64, stream *Stream, operationType OperationType) {
   155  	s.lock.Lock()
   156  	defer s.lock.Unlock()
   157  	s.StreamsByType[operationType][destination] = stream
   158  	s.cleanCanceledStreams(operationType)
   159  }
   160  
   161  func (s *RPC) unMapStream(destination uint64, operationType OperationType) {
   162  	s.lock.Lock()
   163  	defer s.lock.Unlock()
   164  	delete(s.StreamsByType[operationType], destination)
   165  }
   166  
   167  func (s *RPC) cleanCanceledStreams(operationType OperationType) {
   168  	for destination, stream := range s.StreamsByType[operationType] {
   169  		if !stream.Canceled() {
   170  			continue
   171  		}
   172  		s.Logger.Infof("Removing stream %d to %d for channel %s because it is canceled", stream.ID, destination, s.Channel)
   173  		delete(s.StreamsByType[operationType], destination)
   174  	}
   175  }
   176  
   177  func submitMsgLength(request *orderer.SubmitRequest) int {
   178  	if request.Payload == nil {
   179  		return 0
   180  	}
   181  	return len(request.Payload.Payload)
   182  }