github.com/yimialmonte/fabric@v2.1.1+incompatible/gossip/election/election_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package election
     8  
     9  import (
    10  	"fmt"
    11  	"sync"
    12  	"sync/atomic"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/hyperledger/fabric/gossip/util"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/mock"
    19  )
    20  
    21  const (
    22  	testTimeout                       = 5 * time.Second
    23  	testPollInterval                  = time.Millisecond * 300
    24  	testStartupGracePeriod            = time.Millisecond * 500
    25  	testMembershipSampleInterval      = time.Millisecond * 100
    26  	testLeaderAliveThreshold          = time.Millisecond * 500
    27  	testLeaderElectionDuration        = time.Millisecond * 500
    28  	testLeadershipDeclarationInterval = testLeaderAliveThreshold / 2
    29  )
    30  
    31  func init() {
    32  	util.SetupTestLogging()
    33  }
    34  
    35  type msg struct {
    36  	sender   string
    37  	proposal bool
    38  }
    39  
    40  func (m *msg) SenderID() peerID {
    41  	return peerID(m.sender)
    42  }
    43  
    44  func (m *msg) IsProposal() bool {
    45  	return m.proposal
    46  }
    47  
    48  func (m *msg) IsDeclaration() bool {
    49  	return !m.proposal
    50  }
    51  
    52  type peer struct {
    53  	mockedMethods map[string]struct{}
    54  	mock.Mock
    55  	id                 string
    56  	peers              map[string]*peer
    57  	sharedLock         *sync.RWMutex
    58  	msgChan            chan Msg
    59  	leaderFromCallback bool
    60  	callbackInvoked    bool
    61  	lock               sync.RWMutex
    62  	LeaderElectionService
    63  }
    64  
    65  func (p *peer) On(methodName string, arguments ...interface{}) *mock.Call {
    66  	p.sharedLock.Lock()
    67  	defer p.sharedLock.Unlock()
    68  	p.mockedMethods[methodName] = struct{}{}
    69  	return p.Mock.On(methodName, arguments...)
    70  }
    71  
    72  func (p *peer) ID() peerID {
    73  	return peerID(p.id)
    74  }
    75  
    76  func (p *peer) Gossip(m Msg) {
    77  	p.sharedLock.RLock()
    78  	defer p.sharedLock.RUnlock()
    79  
    80  	if _, isMocked := p.mockedMethods["Gossip"]; isMocked {
    81  		p.Called(m)
    82  		return
    83  	}
    84  
    85  	for _, peer := range p.peers {
    86  		if peer.id == p.id {
    87  			continue
    88  		}
    89  		peer.msgChan <- m.(*msg)
    90  	}
    91  }
    92  
    93  func (p *peer) Accept() <-chan Msg {
    94  	p.sharedLock.RLock()
    95  	defer p.sharedLock.RUnlock()
    96  
    97  	if _, isMocked := p.mockedMethods["Accept"]; isMocked {
    98  		args := p.Called()
    99  		return args.Get(0).(<-chan Msg)
   100  	}
   101  	return (<-chan Msg)(p.msgChan)
   102  }
   103  
   104  func (p *peer) CreateMessage(isDeclaration bool) Msg {
   105  	return &msg{proposal: !isDeclaration, sender: p.id}
   106  }
   107  
   108  func (p *peer) Peers() []Peer {
   109  	p.sharedLock.RLock()
   110  	defer p.sharedLock.RUnlock()
   111  
   112  	if _, isMocked := p.mockedMethods["Peers"]; isMocked {
   113  		args := p.Called()
   114  		return args.Get(0).([]Peer)
   115  	}
   116  
   117  	var peers []Peer
   118  	for id := range p.peers {
   119  		peers = append(peers, &peer{id: id})
   120  	}
   121  	return peers
   122  }
   123  
   124  func (p *peer) ReportMetrics(isLeader bool) {
   125  	p.Mock.Called(isLeader)
   126  }
   127  
   128  func (p *peer) leaderCallback(isLeader bool) {
   129  	p.lock.Lock()
   130  	defer p.lock.Unlock()
   131  	p.leaderFromCallback = isLeader
   132  	p.callbackInvoked = true
   133  }
   134  
   135  func (p *peer) isLeaderFromCallback() bool {
   136  	p.lock.RLock()
   137  	defer p.lock.RUnlock()
   138  	return p.leaderFromCallback
   139  }
   140  
   141  func (p *peer) isCallbackInvoked() bool {
   142  	p.lock.RLock()
   143  	defer p.lock.RUnlock()
   144  	return p.callbackInvoked
   145  }
   146  
   147  func createPeers(spawnInterval time.Duration, ids ...int) []*peer {
   148  	peers := make([]*peer, len(ids))
   149  	peerMap := make(map[string]*peer)
   150  	l := &sync.RWMutex{}
   151  	for i, id := range ids {
   152  		p := createPeer(id, peerMap, l)
   153  		if spawnInterval != 0 {
   154  			time.Sleep(spawnInterval)
   155  		}
   156  		peers[i] = p
   157  	}
   158  	return peers
   159  }
   160  
   161  func createPeerWithCostumeMetrics(id int, peerMap map[string]*peer, l *sync.RWMutex, f func(mock.Arguments)) *peer {
   162  	idStr := fmt.Sprintf("p%d", id)
   163  	c := make(chan Msg, 100)
   164  	p := &peer{id: idStr, peers: peerMap, sharedLock: l, msgChan: c, mockedMethods: make(map[string]struct{}), leaderFromCallback: false, callbackInvoked: false}
   165  	p.On("ReportMetrics", mock.Anything).Run(f)
   166  	config := ElectionConfig{
   167  		StartupGracePeriod:       testStartupGracePeriod,
   168  		MembershipSampleInterval: testMembershipSampleInterval,
   169  		LeaderAliveThreshold:     testLeaderAliveThreshold,
   170  		LeaderElectionDuration:   testLeaderElectionDuration,
   171  	}
   172  	p.LeaderElectionService = NewLeaderElectionService(p, idStr, p.leaderCallback, config)
   173  	l.Lock()
   174  	peerMap[idStr] = p
   175  	l.Unlock()
   176  	return p
   177  
   178  }
   179  
   180  func createPeer(id int, peerMap map[string]*peer, l *sync.RWMutex) *peer {
   181  	return createPeerWithCostumeMetrics(id, peerMap, l, func(mock.Arguments) {})
   182  }
   183  
   184  func waitForMultipleLeadersElection(t *testing.T, peers []*peer, leadersNum int) []string {
   185  	end := time.Now().Add(testTimeout)
   186  	for time.Now().Before(end) {
   187  		var leaders []string
   188  		for _, p := range peers {
   189  			if p.IsLeader() {
   190  				leaders = append(leaders, p.id)
   191  			}
   192  		}
   193  		if len(leaders) >= leadersNum {
   194  			return leaders
   195  		}
   196  		time.Sleep(testPollInterval)
   197  	}
   198  	t.Fatal("No leader detected")
   199  	return nil
   200  }
   201  
   202  func waitForLeaderElection(t *testing.T, peers []*peer) []string {
   203  	return waitForMultipleLeadersElection(t, peers, 1)
   204  }
   205  
   206  func TestMetrics(t *testing.T) {
   207  	t.Parallel()
   208  	// Scenario: spawn a single peer and ensure it reports being a leader after some time.
   209  	// Then, make it relinquish its leadership and then ensure it reports not being a leader.
   210  	var wgLeader sync.WaitGroup
   211  	var wgFollower sync.WaitGroup
   212  	wgLeader.Add(1)
   213  	wgFollower.Add(1)
   214  	var once sync.Once
   215  	var once2 sync.Once
   216  	f := func(args mock.Arguments) {
   217  		if args[0] == true {
   218  			once.Do(func() {
   219  				wgLeader.Done()
   220  			})
   221  		} else {
   222  			once2.Do(func() {
   223  				wgFollower.Done()
   224  			})
   225  		}
   226  	}
   227  
   228  	p := createPeerWithCostumeMetrics(0, make(map[string]*peer), &sync.RWMutex{}, f)
   229  	waitForLeaderElection(t, []*peer{p})
   230  
   231  	// Ensure we sent a leadership declaration during the time of leadership acquisition
   232  	wgLeader.Wait()
   233  	p.AssertCalled(t, "ReportMetrics", true)
   234  
   235  	p.Yield()
   236  	assert.False(t, p.IsLeader())
   237  
   238  	// Ensure declaration for not being a leader was sent
   239  	wgFollower.Wait()
   240  	p.AssertCalled(t, "ReportMetrics", false)
   241  
   242  	waitForLeaderElection(t, []*peer{p})
   243  }
   244  
   245  func TestInitPeersAtSameTime(t *testing.T) {
   246  	t.Parallel()
   247  	// Scenario: Peers are spawned at the same time
   248  	// expected outcome: the peer that has the lowest ID is the leader
   249  	peers := createPeers(0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
   250  	time.Sleep(testStartupGracePeriod + testLeaderElectionDuration)
   251  	leaders := waitForLeaderElection(t, peers)
   252  	isP0leader := peers[len(peers)-1].IsLeader()
   253  	assert.True(t, isP0leader, "p0 isn't a leader. Leaders are: %v", leaders)
   254  	assert.Len(t, leaders, 1, "More than 1 leader elected")
   255  	waitForBoolFunc(t, peers[len(peers)-1].isLeaderFromCallback, true, "Leadership callback result is wrong for ", peers[len(peers)-1].id)
   256  }
   257  
   258  func TestInitPeersStartAtIntervals(t *testing.T) {
   259  	t.Parallel()
   260  	// Scenario: Peers are spawned one by one in a slow rate
   261  	// expected outcome: the first peer is the leader although its ID is highest
   262  	peers := createPeers(testStartupGracePeriod+testLeadershipDeclarationInterval, 3, 2, 1, 0)
   263  	waitForLeaderElection(t, peers)
   264  	assert.True(t, peers[0].IsLeader())
   265  }
   266  
   267  func TestStop(t *testing.T) {
   268  	t.Parallel()
   269  	// Scenario: peers are spawned at the same time
   270  	// and then are stopped. We count the number of Gossip() invocations they invoke
   271  	// after they stop, and it should not increase after they are stopped
   272  	peers := createPeers(0, 3, 2, 1, 0)
   273  	var gossipCounter int32
   274  	for i, p := range peers {
   275  		p.On("Gossip", mock.Anything).Run(func(args mock.Arguments) {
   276  			msg := args.Get(0).(Msg)
   277  			atomic.AddInt32(&gossipCounter, int32(1))
   278  			for j := range peers {
   279  				if i == j {
   280  					continue
   281  				}
   282  				peers[j].msgChan <- msg
   283  			}
   284  		})
   285  	}
   286  	waitForLeaderElection(t, peers)
   287  	for _, p := range peers {
   288  		p.Stop()
   289  	}
   290  	time.Sleep(testLeaderAliveThreshold)
   291  	gossipCounterAfterStop := atomic.LoadInt32(&gossipCounter)
   292  	time.Sleep(testLeaderAliveThreshold * 5)
   293  	assert.Equal(t, gossipCounterAfterStop, atomic.LoadInt32(&gossipCounter))
   294  }
   295  
   296  func TestConvergence(t *testing.T) {
   297  	// Scenario: 2 peer group converge their views
   298  	// expected outcome: only 1 leader is left out of the 2
   299  	// and that leader is the leader with the lowest ID
   300  	t.Parallel()
   301  	peers1 := createPeers(0, 3, 2, 1, 0)
   302  	peers2 := createPeers(0, 4, 5, 6, 7)
   303  	leaders1 := waitForLeaderElection(t, peers1)
   304  	leaders2 := waitForLeaderElection(t, peers2)
   305  	assert.Len(t, leaders1, 1, "Peer group 1 was suppose to have 1 leader exactly")
   306  	assert.Len(t, leaders2, 1, "Peer group 2 was suppose to have 1 leader exactly")
   307  	combinedPeers := append(peers1, peers2...)
   308  
   309  	var allPeerIds []Peer
   310  	for _, p := range combinedPeers {
   311  		allPeerIds = append(allPeerIds, &peer{id: p.id})
   312  	}
   313  
   314  	for i, p := range combinedPeers {
   315  		index := i
   316  		gossipFunc := func(args mock.Arguments) {
   317  			msg := args.Get(0).(Msg)
   318  			for j := range combinedPeers {
   319  				if index == j {
   320  					continue
   321  				}
   322  				combinedPeers[j].msgChan <- msg
   323  			}
   324  		}
   325  		p.On("Gossip", mock.Anything).Run(gossipFunc)
   326  		p.On("Peers").Return(allPeerIds)
   327  	}
   328  
   329  	time.Sleep(testLeaderAliveThreshold * 5)
   330  	finalLeaders := waitForLeaderElection(t, combinedPeers)
   331  	assert.Len(t, finalLeaders, 1, "Combined peer group was suppose to have 1 leader exactly")
   332  	assert.Equal(t, leaders1[0], finalLeaders[0], "Combined peer group has different leader than expected:")
   333  
   334  	for _, p := range combinedPeers {
   335  		if p.id == finalLeaders[0] {
   336  			waitForBoolFunc(t, p.isLeaderFromCallback, true, "Leadership callback result is wrong for ", p.id)
   337  			waitForBoolFunc(t, p.isCallbackInvoked, true, "Leadership callback wasn't invoked for ", p.id)
   338  		} else {
   339  			waitForBoolFunc(t, p.isLeaderFromCallback, false, "Leadership callback result is wrong for ", p.id)
   340  			if p.id == leaders2[0] {
   341  				waitForBoolFunc(t, p.isCallbackInvoked, true, "Leadership callback wasn't invoked for ", p.id)
   342  			}
   343  		}
   344  	}
   345  }
   346  
   347  func TestLeadershipTakeover(t *testing.T) {
   348  	t.Parallel()
   349  	// Scenario: Peers spawn one by one in descending order.
   350  	// After a while, the leader peer stops.
   351  	// expected outcome: the peer that takes over is the peer with lowest ID
   352  	peers := createPeers(testStartupGracePeriod+testLeadershipDeclarationInterval, 5, 4, 3, 2)
   353  	leaders := waitForLeaderElection(t, peers)
   354  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   355  	assert.Equal(t, "p5", leaders[0])
   356  	peers[0].Stop()
   357  	time.Sleep(testLeadershipDeclarationInterval + testLeaderAliveThreshold*3)
   358  	leaders = waitForLeaderElection(t, peers[1:])
   359  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   360  	assert.Equal(t, "p2", leaders[0])
   361  }
   362  
   363  func TestYield(t *testing.T) {
   364  	t.Parallel()
   365  	// Scenario: Peers spawn and a leader is elected.
   366  	// After a while, the leader yields.
   367  	// (Call yield twice to ensure only one callback is called)
   368  	// Expected outcome:
   369  	// (1) A new leader is elected
   370  	// (2) The old leader doesn't take back its leadership
   371  	peers := createPeers(0, 0, 1, 2, 3, 4, 5)
   372  	leaders := waitForLeaderElection(t, peers)
   373  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   374  	assert.Equal(t, "p0", leaders[0])
   375  	peers[0].Yield()
   376  	// Ensure the callback was called with 'false'
   377  	assert.True(t, peers[0].isCallbackInvoked())
   378  	assert.False(t, peers[0].isLeaderFromCallback())
   379  	// Clear the callback invoked flag
   380  	peers[0].lock.Lock()
   381  	peers[0].callbackInvoked = false
   382  	peers[0].lock.Unlock()
   383  	// Yield again and ensure it isn't called again
   384  	peers[0].Yield()
   385  	assert.False(t, peers[0].isCallbackInvoked())
   386  
   387  	ensureP0isNotAleader := func() bool {
   388  		leaders := waitForLeaderElection(t, peers)
   389  		return len(leaders) == 1 && leaders[0] != "p0"
   390  	}
   391  	// A new leader is elected, and it is not p0
   392  	waitForBoolFunc(t, ensureP0isNotAleader, true)
   393  	time.Sleep(testLeaderAliveThreshold * 2)
   394  	// After a while, p0 doesn't restore its leadership status
   395  	waitForBoolFunc(t, ensureP0isNotAleader, true)
   396  }
   397  
   398  func TestYieldSinglePeer(t *testing.T) {
   399  	t.Parallel()
   400  	// Scenario: spawn a single peer and have it yield.
   401  	// Ensure it recovers its leadership after a while.
   402  	peers := createPeers(0, 0)
   403  	waitForLeaderElection(t, peers)
   404  	peers[0].Yield()
   405  	assert.False(t, peers[0].IsLeader())
   406  	waitForLeaderElection(t, peers)
   407  }
   408  
   409  func TestYieldAllPeers(t *testing.T) {
   410  	t.Parallel()
   411  	// Scenario: spawn 2 peers and have them all yield after regaining leadership.
   412  	// Ensure the first peer is the leader in the end after both peers yield
   413  	peers := createPeers(0, 0, 1)
   414  	leaders := waitForLeaderElection(t, peers)
   415  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   416  	assert.Equal(t, "p0", leaders[0])
   417  	peers[0].Yield()
   418  	leaders = waitForLeaderElection(t, peers)
   419  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   420  	assert.Equal(t, "p1", leaders[0])
   421  	peers[1].Yield()
   422  	leaders = waitForLeaderElection(t, peers)
   423  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   424  	assert.Equal(t, "p0", leaders[0])
   425  }
   426  
   427  func TestPartition(t *testing.T) {
   428  	t.Parallel()
   429  	// Scenario: peers spawn together, and then after a while a network partition occurs
   430  	// and no peer can communicate with another peer
   431  	// Expected outcome 1: each peer is a leader
   432  	// After this, we heal the partition to be a unified view again
   433  	// Expected outcome 2: p0 is the leader once again
   434  	peers := createPeers(0, 5, 4, 3, 2, 1, 0)
   435  	leaders := waitForLeaderElection(t, peers)
   436  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   437  	assert.Equal(t, "p0", leaders[0])
   438  	waitForBoolFunc(t, peers[len(peers)-1].isLeaderFromCallback, true, "Leadership callback result is wrong for %s", peers[len(peers)-1].id)
   439  
   440  	for _, p := range peers {
   441  		p.On("Peers").Return([]Peer{})
   442  		p.On("Gossip", mock.Anything)
   443  	}
   444  	time.Sleep(testLeadershipDeclarationInterval + testLeaderAliveThreshold*2)
   445  	leaders = waitForMultipleLeadersElection(t, peers, 6)
   446  	assert.Len(t, leaders, 6)
   447  	for _, p := range peers {
   448  		waitForBoolFunc(t, p.isLeaderFromCallback, true, "Leadership callback result is wrong for %s", p.id)
   449  	}
   450  
   451  	for _, p := range peers {
   452  		p.sharedLock.Lock()
   453  		p.mockedMethods = make(map[string]struct{})
   454  		p.callbackInvoked = false
   455  		p.sharedLock.Unlock()
   456  	}
   457  	time.Sleep(testLeadershipDeclarationInterval + testLeaderAliveThreshold*2)
   458  	leaders = waitForLeaderElection(t, peers)
   459  	assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
   460  	assert.Equal(t, "p0", leaders[0])
   461  	for _, p := range peers {
   462  		if p.id == leaders[0] {
   463  			waitForBoolFunc(t, p.isLeaderFromCallback, true, "Leadership callback result is wrong for %s", p.id)
   464  		} else {
   465  			waitForBoolFunc(t, p.isLeaderFromCallback, false, "Leadership callback result is wrong for %s", p.id)
   466  			waitForBoolFunc(t, p.isCallbackInvoked, true, "Leadership callback wasn't invoked for %s", p.id)
   467  		}
   468  	}
   469  }
   470  
   471  func Test_peerIDString(t *testing.T) {
   472  	tests := []struct {
   473  		input    peerID
   474  		expected string
   475  	}{
   476  		{nil, "<nil>"},
   477  		{peerID{}, ""},
   478  		{peerID{0, 1, 2, 3}, "00010203"},
   479  	}
   480  	for _, tt := range tests {
   481  		assert.Equal(t, tt.expected, tt.input.String())
   482  	}
   483  }
   484  
   485  func waitForBoolFunc(t *testing.T, f func() bool, expectedValue bool, msgAndArgs ...interface{}) {
   486  	end := time.Now().Add(testTimeout)
   487  	for time.Now().Before(end) {
   488  		if f() == expectedValue {
   489  			return
   490  		}
   491  		time.Sleep(testPollInterval)
   492  	}
   493  	assert.Fail(t, fmt.Sprintf("Should be %t", expectedValue), msgAndArgs...)
   494  }