github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/orderer/common/cluster/rpc_test.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package cluster_test
     8  
     9  import (
    10  	"context"
    11  	"io"
    12  	"sync"
    13  	"sync/atomic"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/hyperledger/fabric-protos-go/common"
    18  	"github.com/hyperledger/fabric-protos-go/orderer"
    19  	"github.com/hyperledger/fabric/common/flogging"
    20  	"github.com/hyperledger/fabric/common/metrics/disabled"
    21  	"github.com/hyperledger/fabric/orderer/common/cluster"
    22  	"github.com/hyperledger/fabric/orderer/common/cluster/mocks"
    23  	"github.com/pkg/errors"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/mock"
    26  	"google.golang.org/grpc"
    27  )
    28  
    29  func TestRPCChangeDestination(t *testing.T) {
    30  	t.Parallel()
    31  	// We send a Submit() to 2 different nodes - 1 and 2.
    32  	// The first invocation of Submit() establishes a stream with node 1
    33  	// and the second establishes a stream with node 2.
    34  	// We define a mock behavior for only a single invocation of Send() on each
    35  	// of the streams (to node 1 and to node 2), therefore we test that invocation
    36  	// of rpc.SendSubmit to node 2 doesn't send the message to node 1.
    37  	comm := &mocks.Communicator{}
    38  
    39  	client1 := &mocks.ClusterClient{}
    40  	client2 := &mocks.ClusterClient{}
    41  
    42  	metrics := cluster.NewMetrics(&disabled.Provider{})
    43  
    44  	comm.On("Remote", "mychannel", uint64(1)).Return(&cluster.RemoteContext{
    45  		SendBuffSize: 10,
    46  		Metrics:      metrics,
    47  		Logger:       flogging.MustGetLogger("test"),
    48  		Client:       client1,
    49  		ProbeConn:    func(_ *grpc.ClientConn) error { return nil },
    50  	}, nil)
    51  	comm.On("Remote", "mychannel", uint64(2)).Return(&cluster.RemoteContext{
    52  		SendBuffSize: 10,
    53  		Metrics:      metrics,
    54  		Logger:       flogging.MustGetLogger("test"),
    55  		Client:       client2,
    56  		ProbeConn:    func(_ *grpc.ClientConn) error { return nil },
    57  	}, nil)
    58  
    59  	streamToNode1 := &mocks.StepClient{}
    60  	streamToNode2 := &mocks.StepClient{}
    61  	streamToNode1.On("Context", mock.Anything).Return(context.Background())
    62  	streamToNode2.On("Context", mock.Anything).Return(context.Background())
    63  
    64  	client1.On("Step", mock.Anything).Return(streamToNode1, nil).Once()
    65  	client2.On("Step", mock.Anything).Return(streamToNode2, nil).Once()
    66  
    67  	rpc := &cluster.RPC{
    68  		Logger:        flogging.MustGetLogger("test"),
    69  		Timeout:       time.Hour,
    70  		StreamsByType: cluster.NewStreamsByType(),
    71  		Channel:       "mychannel",
    72  		Comm:          comm,
    73  	}
    74  
    75  	var sent sync.WaitGroup
    76  	sent.Add(2)
    77  
    78  	signalSent := func(_ mock.Arguments) {
    79  		sent.Done()
    80  	}
    81  	streamToNode1.On("Send", mock.Anything).Return(nil).Run(signalSent).Once()
    82  	streamToNode2.On("Send", mock.Anything).Return(nil).Run(signalSent).Once()
    83  	streamToNode1.On("Recv").Return(nil, io.EOF)
    84  	streamToNode2.On("Recv").Return(nil, io.EOF)
    85  
    86  	rpc.SendSubmit(1, &orderer.SubmitRequest{Channel: "mychannel"})
    87  	rpc.SendSubmit(2, &orderer.SubmitRequest{Channel: "mychannel"})
    88  
    89  	sent.Wait()
    90  	streamToNode1.AssertNumberOfCalls(t, "Send", 1)
    91  	streamToNode2.AssertNumberOfCalls(t, "Send", 1)
    92  }
    93  
    94  func TestSend(t *testing.T) {
    95  	t.Parallel()
    96  	submitRequest := &orderer.SubmitRequest{Channel: "mychannel"}
    97  	submitResponse := &orderer.StepResponse{
    98  		Payload: &orderer.StepResponse_SubmitRes{
    99  			SubmitRes: &orderer.SubmitResponse{Status: common.Status_SUCCESS},
   100  		},
   101  	}
   102  
   103  	consensusRequest := &orderer.ConsensusRequest{
   104  		Channel: "mychannel",
   105  	}
   106  
   107  	submitReq := wrapSubmitReq(submitRequest)
   108  
   109  	consensusReq := &orderer.StepRequest{
   110  		Payload: &orderer.StepRequest_ConsensusRequest{
   111  			ConsensusRequest: consensusRequest,
   112  		},
   113  	}
   114  
   115  	submit := func(rpc *cluster.RPC) error {
   116  		err := rpc.SendSubmit(1, submitRequest)
   117  		return err
   118  	}
   119  
   120  	step := func(rpc *cluster.RPC) error {
   121  		return rpc.SendConsensus(1, consensusRequest)
   122  	}
   123  
   124  	type testCase struct {
   125  		name           string
   126  		method         func(rpc *cluster.RPC) error
   127  		sendReturns    error
   128  		sendCalledWith *orderer.StepRequest
   129  		receiveReturns []interface{}
   130  		stepReturns    []interface{}
   131  		remoteError    error
   132  		expectedErr    string
   133  	}
   134  
   135  	l := &sync.Mutex{}
   136  	var tst testCase
   137  
   138  	sent := make(chan struct{})
   139  
   140  	var sendCalls uint32
   141  
   142  	stream := &mocks.StepClient{}
   143  	stream.On("Context", mock.Anything).Return(context.Background())
   144  	stream.On("Send", mock.Anything).Return(func(*orderer.StepRequest) error {
   145  		l.Lock()
   146  		defer l.Unlock()
   147  		atomic.AddUint32(&sendCalls, 1)
   148  		sent <- struct{}{}
   149  		return tst.sendReturns
   150  	})
   151  
   152  	for _, tst := range []testCase{
   153  		{
   154  			name:           "Send and Receive submit succeed",
   155  			method:         submit,
   156  			sendReturns:    nil,
   157  			stepReturns:    []interface{}{stream, nil},
   158  			receiveReturns: []interface{}{submitResponse, nil},
   159  			sendCalledWith: submitReq,
   160  		},
   161  		{
   162  			name:           "Send step succeed",
   163  			method:         step,
   164  			sendReturns:    nil,
   165  			stepReturns:    []interface{}{stream, nil},
   166  			sendCalledWith: consensusReq,
   167  		},
   168  		{
   169  			name:           "Send submit fails",
   170  			method:         submit,
   171  			sendReturns:    errors.New("oops"),
   172  			stepReturns:    []interface{}{stream, nil},
   173  			sendCalledWith: submitReq,
   174  			expectedErr:    "stream is aborted",
   175  		},
   176  		{
   177  			name:           "Send step fails",
   178  			method:         step,
   179  			sendReturns:    errors.New("oops"),
   180  			stepReturns:    []interface{}{stream, nil},
   181  			sendCalledWith: consensusReq,
   182  			expectedErr:    "stream is aborted",
   183  		},
   184  		{
   185  			name:        "Remote() fails",
   186  			method:      submit,
   187  			remoteError: errors.New("timed out"),
   188  			stepReturns: []interface{}{stream, nil},
   189  			expectedErr: "timed out",
   190  		},
   191  		{
   192  			name:        "Submit fails with Send",
   193  			method:      submit,
   194  			stepReturns: []interface{}{nil, errors.New("deadline exceeded")},
   195  			expectedErr: "deadline exceeded",
   196  		},
   197  	} {
   198  		l.Lock()
   199  		testCase := tst
   200  		l.Unlock()
   201  
   202  		t.Run(testCase.name, func(t *testing.T) {
   203  			atomic.StoreUint32(&sendCalls, 0)
   204  			isSend := testCase.receiveReturns == nil
   205  			comm := &mocks.Communicator{}
   206  			client := &mocks.ClusterClient{}
   207  			client.On("Step", mock.Anything).Return(testCase.stepReturns...)
   208  			rm := &cluster.RemoteContext{
   209  				Metrics:      cluster.NewMetrics(&disabled.Provider{}),
   210  				SendBuffSize: 1,
   211  				Logger:       flogging.MustGetLogger("test"),
   212  				ProbeConn:    func(_ *grpc.ClientConn) error { return nil },
   213  				Client:       client,
   214  			}
   215  			defer rm.Abort()
   216  			comm.On("Remote", "mychannel", uint64(1)).Return(rm, testCase.remoteError)
   217  
   218  			rpc := &cluster.RPC{
   219  				Logger:        flogging.MustGetLogger("test"),
   220  				Timeout:       time.Hour,
   221  				StreamsByType: cluster.NewStreamsByType(),
   222  				Channel:       "mychannel",
   223  				Comm:          comm,
   224  			}
   225  
   226  			var err error
   227  
   228  			err = testCase.method(rpc)
   229  			if testCase.remoteError == nil && testCase.stepReturns[1] == nil {
   230  				<-sent
   231  			}
   232  
   233  			if testCase.stepReturns[1] == nil && testCase.remoteError == nil {
   234  				assert.NoError(t, err)
   235  			} else {
   236  				assert.EqualError(t, err, testCase.expectedErr)
   237  			}
   238  
   239  			if testCase.remoteError == nil && testCase.expectedErr == "" && isSend {
   240  				stream.AssertCalled(t, "Send", testCase.sendCalledWith)
   241  				// Ensure that if we succeeded - only 1 stream was created despite 2 calls
   242  				// to Send() were made
   243  				err := testCase.method(rpc)
   244  				<-sent
   245  
   246  				assert.NoError(t, err)
   247  				assert.Equal(t, 2, int(atomic.LoadUint32(&sendCalls)))
   248  				client.AssertNumberOfCalls(t, "Step", 1)
   249  			}
   250  		})
   251  	}
   252  }
   253  
   254  func TestRPCGarbageCollection(t *testing.T) {
   255  	// Scenario: Send a message to a remote node, and establish a stream
   256  	// while doing it.
   257  	// Afterwards - make that stream be aborted, and send a message to a different
   258  	// remote node.
   259  	// The first stream should be cleaned from the mapping.
   260  
   261  	t.Parallel()
   262  
   263  	comm := &mocks.Communicator{}
   264  	client := &mocks.ClusterClient{}
   265  	stream := &mocks.StepClient{}
   266  
   267  	remote := &cluster.RemoteContext{
   268  		SendBuffSize: 10,
   269  		Metrics:      cluster.NewMetrics(&disabled.Provider{}),
   270  		Logger:       flogging.MustGetLogger("test"),
   271  		Client:       client,
   272  		ProbeConn:    func(_ *grpc.ClientConn) error { return nil },
   273  	}
   274  
   275  	var sent sync.WaitGroup
   276  
   277  	defineMocks := func(destination uint64) {
   278  		sent.Add(1)
   279  		comm.On("Remote", "mychannel", destination).Return(remote, nil)
   280  		stream.On("Context", mock.Anything).Return(context.Background())
   281  		client.On("Step", mock.Anything).Return(stream, nil).Once()
   282  		stream.On("Send", mock.Anything).Return(nil).Once().Run(func(_ mock.Arguments) {
   283  			sent.Done()
   284  		})
   285  		stream.On("Recv").Return(nil, nil)
   286  	}
   287  
   288  	mapping := cluster.NewStreamsByType()
   289  
   290  	rpc := &cluster.RPC{
   291  		Logger:        flogging.MustGetLogger("test"),
   292  		Timeout:       time.Hour,
   293  		StreamsByType: mapping,
   294  		Channel:       "mychannel",
   295  		Comm:          comm,
   296  	}
   297  
   298  	defineMocks(1)
   299  
   300  	rpc.SendSubmit(1, &orderer.SubmitRequest{Channel: "mychannel"})
   301  	// Wait for the message to arrive
   302  	sent.Wait()
   303  	// Ensure the stream is initialized in the mapping
   304  	assert.Len(t, mapping[cluster.SubmitOperation], 1)
   305  	assert.Equal(t, uint64(1), mapping[cluster.SubmitOperation][1].ID)
   306  	// And the underlying gRPC stream indeed had Send invoked on it.
   307  	stream.AssertNumberOfCalls(t, "Send", 1)
   308  
   309  	// Abort all streams we currently have that are associated to the remote.
   310  	remote.Abort()
   311  
   312  	// The stream still exists, as it is not cleaned yet.
   313  	assert.Len(t, mapping[cluster.SubmitOperation], 1)
   314  	assert.Equal(t, uint64(1), mapping[cluster.SubmitOperation][1].ID)
   315  
   316  	// Prepare for the next transmission.
   317  	defineMocks(2)
   318  
   319  	// Send a message to a different node.
   320  	rpc.SendSubmit(2, &orderer.SubmitRequest{Channel: "mychannel"})
   321  	// The mapping should be now cleaned from the previous stream.
   322  	assert.Len(t, mapping[cluster.SubmitOperation], 1)
   323  	assert.Equal(t, uint64(2), mapping[cluster.SubmitOperation][2].ID)
   324  }