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