github.com/ewagmig/fabric@v2.1.1+incompatible/gossip/service/integration_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package service
     8  
     9  import (
    10  	"bytes"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/hyperledger/fabric/common/flogging"
    16  	"github.com/hyperledger/fabric/core/deliverservice"
    17  	"github.com/hyperledger/fabric/gossip/api"
    18  	"github.com/hyperledger/fabric/gossip/election"
    19  	"github.com/hyperledger/fabric/gossip/util"
    20  	"github.com/hyperledger/fabric/internal/pkg/comm"
    21  	"github.com/hyperledger/fabric/internal/pkg/peer/blocksprovider"
    22  	"github.com/hyperledger/fabric/internal/pkg/peer/orderers"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  type embeddingDeliveryService struct {
    28  	startOnce sync.Once
    29  	stopOnce  sync.Once
    30  	deliverservice.DeliverService
    31  	startSignal sync.WaitGroup
    32  	stopSignal  sync.WaitGroup
    33  }
    34  
    35  func newEmbeddingDeliveryService(ds deliverservice.DeliverService) *embeddingDeliveryService {
    36  	eds := &embeddingDeliveryService{
    37  		DeliverService: ds,
    38  	}
    39  	eds.startSignal.Add(1)
    40  	eds.stopSignal.Add(1)
    41  	return eds
    42  }
    43  
    44  func (eds *embeddingDeliveryService) waitForDeliveryServiceActivation() {
    45  	eds.startSignal.Wait()
    46  }
    47  
    48  func (eds *embeddingDeliveryService) waitForDeliveryServiceTermination() {
    49  	eds.stopSignal.Wait()
    50  }
    51  
    52  func (eds *embeddingDeliveryService) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {
    53  	eds.startOnce.Do(func() {
    54  		eds.startSignal.Done()
    55  	})
    56  	return eds.DeliverService.StartDeliverForChannel(chainID, ledgerInfo, finalizer)
    57  }
    58  
    59  func (eds *embeddingDeliveryService) StopDeliverForChannel(chainID string) error {
    60  	eds.stopOnce.Do(func() {
    61  		eds.stopSignal.Done()
    62  	})
    63  	return eds.DeliverService.StopDeliverForChannel(chainID)
    64  }
    65  
    66  func (eds *embeddingDeliveryService) Stop() {
    67  	eds.DeliverService.Stop()
    68  }
    69  
    70  type embeddingDeliveryServiceFactory struct {
    71  	DeliveryServiceFactory
    72  }
    73  
    74  func (edsf *embeddingDeliveryServiceFactory) Service(g GossipServiceAdapter, endpoints *orderers.ConnectionSource, mcs api.MessageCryptoService, isStaticLeader bool) deliverservice.DeliverService {
    75  	ds := edsf.DeliveryServiceFactory.Service(g, endpoints, mcs, false)
    76  	return newEmbeddingDeliveryService(ds)
    77  }
    78  
    79  func TestLeaderYield(t *testing.T) {
    80  	// Scenario: Spawn 2 peers and wait for the first one to be the leader
    81  	// There isn't any orderer present so the leader peer won't be able to
    82  	// connect to the orderer, and should relinquish its leadership after a while.
    83  	// Make sure the other peer declares itself as the leader soon after.
    84  	takeOverMaxTimeout := time.Minute
    85  	// It's enough to make single re-try
    86  	// There is no ordering service available anyway, hence connection timeout
    87  	// could be shorter
    88  	serviceConfig := &ServiceConfig{
    89  		UseLeaderElection:          true,
    90  		OrgLeader:                  false,
    91  		ElectionStartupGracePeriod: election.DefStartupGracePeriod,
    92  		// Since we ensuring gossip has stable membership, there is no need for
    93  		// leader election to wait for stabilization
    94  		ElectionMembershipSampleInterval: time.Millisecond * 100,
    95  		ElectionLeaderAliveThreshold:     time.Second * 5,
    96  		// Test case has only two instance + making assertions only after membership view
    97  		// is stable, hence election duration could be shorter
    98  		ElectionLeaderElectionDuration: time.Millisecond * 500,
    99  	}
   100  	n := 2
   101  	gossips := startPeers(serviceConfig, n, 0, 1)
   102  	defer stopPeers(gossips)
   103  	channelName := "channelA"
   104  	peerIndexes := []int{0, 1}
   105  	// Add peers to the channel
   106  	addPeersToChannel(channelName, gossips, peerIndexes)
   107  	// Prime the membership view of the peers
   108  	waitForFullMembershipOrFailNow(t, channelName, gossips, n, time.Second*30, time.Millisecond*100)
   109  
   110  	grpcClient, err := comm.NewGRPCClient(comm.ClientConfig{})
   111  	require.NoError(t, err)
   112  
   113  	store := newTransientStore(t)
   114  	defer store.tearDown()
   115  
   116  	// Helper function that creates a gossipService instance
   117  	newGossipService := func(i int) *GossipService {
   118  		gs := gossips[i].GossipService
   119  		gs.deliveryFactory = &embeddingDeliveryServiceFactory{&deliveryFactoryImpl{
   120  			credentialSupport: comm.NewCredentialSupport(),
   121  			deliverServiceConfig: &deliverservice.DeliverServiceConfig{
   122  				PeerTLSEnabled:              false,
   123  				ReConnectBackoffThreshold:   deliverservice.DefaultReConnectBackoffThreshold,
   124  				ReconnectTotalTimeThreshold: time.Second,
   125  				ConnectionTimeout:           time.Millisecond * 100,
   126  			},
   127  			deliverGRPCClient: grpcClient,
   128  		}}
   129  		gs.InitializeChannel(channelName, orderers.NewConnectionSource(flogging.MustGetLogger("peer.orderers"), nil), store.Store, Support{
   130  			Committer: &mockLedgerInfo{1},
   131  		})
   132  		return gs
   133  	}
   134  
   135  	// The first leader is determined by the peer with the lower PKIid (lower TCP port in this case).
   136  	// We set p0 to be the peer with the lower PKIid to ensure it'll be elected as leader before p1 and spare time.
   137  	pkiID0 := gossips[0].peerIdentity
   138  	pkiID1 := gossips[1].peerIdentity
   139  	var firstLeaderIdx, secondLeaderIdx int
   140  	if bytes.Compare(pkiID0, pkiID1) < 0 {
   141  		firstLeaderIdx = 0
   142  		secondLeaderIdx = 1
   143  	} else {
   144  		firstLeaderIdx = 1
   145  		secondLeaderIdx = 0
   146  	}
   147  	p0 := newGossipService(firstLeaderIdx)
   148  	p1 := newGossipService(secondLeaderIdx)
   149  
   150  	// Returns index of the leader or -1 if no leader elected
   151  	getLeader := func() int {
   152  		p0.lock.RLock()
   153  		p1.lock.RLock()
   154  		defer p0.lock.RUnlock()
   155  		defer p1.lock.RUnlock()
   156  
   157  		if p0.leaderElection[channelName].IsLeader() {
   158  			return 0
   159  		}
   160  		if p1.leaderElection[channelName].IsLeader() {
   161  			return 1
   162  		}
   163  		return -1
   164  	}
   165  
   166  	ds0 := p0.deliveryService[channelName].(*embeddingDeliveryService)
   167  
   168  	// Wait for p0 to connect to the ordering service
   169  	ds0.waitForDeliveryServiceActivation()
   170  	t.Log("p0 started its delivery service")
   171  	// Ensure it's a leader
   172  	assert.Equal(t, 0, getLeader())
   173  	// Wait for p0 to lose its leadership
   174  	ds0.waitForDeliveryServiceTermination()
   175  	t.Log("p0 stopped its delivery service")
   176  	// Ensure p0 is not a leader
   177  	assert.NotEqual(t, 0, getLeader())
   178  	// Wait for p1 to take over. It should take over before time reaches timeLimit
   179  	timeLimit := time.Now().Add(takeOverMaxTimeout)
   180  	for getLeader() != 1 && time.Now().Before(timeLimit) {
   181  		time.Sleep(100 * time.Millisecond)
   182  	}
   183  	if time.Now().After(timeLimit) && getLeader() != 1 {
   184  		util.PrintStackTrace()
   185  		t.Fatalf("p1 hasn't taken over leadership within %v: %d", takeOverMaxTimeout, getLeader())
   186  	}
   187  	t.Log("p1 has taken over leadership")
   188  	p0.chains[channelName].Stop()
   189  	p1.chains[channelName].Stop()
   190  	p0.deliveryService[channelName].Stop()
   191  	p1.deliveryService[channelName].Stop()
   192  }