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