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 }