github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/orderer/common/cluster/connections_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  	"sync"
    11  	"testing"
    12  
    13  	"github.com/hyperledger/fabric/common/metrics/disabled"
    14  	"github.com/hyperledger/fabric/orderer/common/cluster"
    15  	"github.com/hyperledger/fabric/orderer/common/cluster/mocks"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/mock"
    18  	"google.golang.org/grpc"
    19  )
    20  
    21  func TestConcurrentConnections(t *testing.T) {
    22  	t.Parallel()
    23  	// Scenario: Have 100 goroutines try to create a connection together at the same time,
    24  	// wait until one of them succeeds, and then wait until they all return,
    25  	// and also ensure they all return the same connection reference
    26  	n := 100
    27  	var wg sync.WaitGroup
    28  	wg.Add(n)
    29  	dialer := &mocks.SecureDialer{}
    30  	conn := &grpc.ClientConn{}
    31  	dialer.On("Dial", mock.Anything, mock.Anything).Return(conn, nil)
    32  	connStore := cluster.NewConnectionStore(dialer, &disabled.Gauge{})
    33  	connect := func() {
    34  		defer wg.Done()
    35  		conn2, err := connStore.Connection("", nil)
    36  		assert.NoError(t, err)
    37  		assert.True(t, conn2 == conn)
    38  	}
    39  	for i := 0; i < n; i++ {
    40  		go connect()
    41  	}
    42  	wg.Wait()
    43  	dialer.AssertNumberOfCalls(t, "Dial", 1)
    44  }
    45  
    46  type connectionMapperSpy struct {
    47  	lookupDelay   chan struct{}
    48  	lookupInvoked chan struct{}
    49  	cluster.ConnectionMapper
    50  }
    51  
    52  func (cms *connectionMapperSpy) Lookup(cert []byte) (*grpc.ClientConn, bool) {
    53  	// Signal that Lookup() has been invoked
    54  	cms.lookupInvoked <- struct{}{}
    55  	// Wait for the main test to signal to advance.
    56  	// This is needed because we need to ensure that all instances
    57  	// of the connectionMapperSpy invoked Lookup()
    58  	<-cms.lookupDelay
    59  	return cms.ConnectionMapper.Lookup(cert)
    60  }
    61  
    62  func TestConcurrentLookupMiss(t *testing.T) {
    63  	t.Parallel()
    64  	// Scenario: 2 concurrent connection attempts are made,
    65  	// and the first 2 Lookup operations are delayed,
    66  	// which makes the connection store attempt to connect
    67  	// at the same time twice.
    68  	// A single connection should be created regardless.
    69  
    70  	dialer := &mocks.SecureDialer{}
    71  	conn := &grpc.ClientConn{}
    72  	dialer.On("Dial", mock.Anything, mock.Anything).Return(conn, nil)
    73  
    74  	connStore := cluster.NewConnectionStore(dialer, &disabled.Gauge{})
    75  	// Wrap the connection mapping with a spy that intercepts Lookup() invocations
    76  	spy := &connectionMapperSpy{
    77  		ConnectionMapper: connStore.Connections,
    78  		lookupDelay:      make(chan struct{}, 2),
    79  		lookupInvoked:    make(chan struct{}, 2),
    80  	}
    81  	connStore.Connections = spy
    82  
    83  	var goroutinesExited sync.WaitGroup
    84  	goroutinesExited.Add(2)
    85  
    86  	for i := 0; i < 2; i++ {
    87  		go func() {
    88  			defer goroutinesExited.Done()
    89  			conn2, err := connStore.Connection("", nil)
    90  			assert.NoError(t, err)
    91  			// Ensure all calls for Connection() return the same reference
    92  			// of the gRPC connection.
    93  			assert.True(t, conn2 == conn)
    94  		}()
    95  	}
    96  	// Wait for the Lookup() to be invoked by both
    97  	// goroutines
    98  	<-spy.lookupInvoked
    99  	<-spy.lookupInvoked
   100  	// Signal the goroutines to finish the Lookup() invocations
   101  	spy.lookupDelay <- struct{}{}
   102  	spy.lookupDelay <- struct{}{}
   103  	// Close the channel so that subsequent Lookup() operations won't be blocked
   104  	close(spy.lookupDelay)
   105  	// Wait for all goroutines to exit
   106  	goroutinesExited.Wait()
   107  }