Skip to content

Commit 035c51c

Browse files
[FAB-2630] Integration election with core.yaml
Leader election configuration parameters should be set using core.yaml and be configurable by various tests. Change-Id: Ief5d0a4ab0fdfe99669bd8577063127b71fe77c0 Signed-off-by: Gennady Laventman <[email protected]>
1 parent ebe1b4d commit 035c51c

File tree

3 files changed

+108
-27
lines changed

3 files changed

+108
-27
lines changed

gossip/election/election.go

+42-13
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,7 @@ import (
2525

2626
"github.com/hyperledger/fabric/gossip/util"
2727
"github.com/op/go-logging"
28-
)
29-
30-
var (
31-
startupGracePeriod = time.Second * 15
32-
membershipSampleInterval = time.Second
33-
leaderAliveThreshold = time.Second * 10
34-
leadershipDeclarationInterval = leaderAliveThreshold / 2
35-
leaderElectionDuration = time.Second * 5
28+
"github.com/spf13/viper"
3629
)
3730

3831
// Gossip leader election module
@@ -178,7 +171,7 @@ type leaderElectionSvcImpl struct {
178171
func (le *leaderElectionSvcImpl) start() {
179172
le.stopWG.Add(2)
180173
go le.handleMessages()
181-
le.waitForMembershipStabilization(startupGracePeriod)
174+
le.waitForMembershipStabilization(getStartupGracePeriod())
182175
go le.run()
183176
}
184177

@@ -273,7 +266,7 @@ func (le *leaderElectionSvcImpl) leaderElection() {
273266
le.logger.Debug(le.id, ": Entering")
274267
defer le.logger.Debug(le.id, ": Exiting")
275268
le.propose()
276-
le.waitForInterrupt(leaderElectionDuration)
269+
le.waitForInterrupt(getLeaderElectionDuration())
277270
// If someone declared itself as a leader, give up
278271
// on trying to become a leader too
279272
if le.isLeaderExists() {
@@ -309,7 +302,7 @@ func (le *leaderElectionSvcImpl) follower() {
309302
le.proposals.Clear()
310303
atomic.StoreInt32(&le.leaderExists, int32(0))
311304
select {
312-
case <-time.After(leaderAliveThreshold):
305+
case <-time.After(getLeaderAliveThreshold()):
313306
case <-le.stopChan:
314307
le.stopChan <- struct{}{}
315308
}
@@ -318,7 +311,7 @@ func (le *leaderElectionSvcImpl) follower() {
318311
func (le *leaderElectionSvcImpl) leader() {
319312
leaderDeclaration := le.adapter.CreateMessage(true)
320313
le.adapter.Gossip(leaderDeclaration)
321-
le.waitForInterrupt(leadershipDeclarationInterval)
314+
le.waitForInterrupt(getLeadershipDeclarationInterval())
322315
}
323316

324317
// waitForMembershipStabilization waits for membership view to stabilize
@@ -329,7 +322,7 @@ func (le *leaderElectionSvcImpl) waitForMembershipStabilization(timeLimit time.D
329322
endTime := time.Now().Add(timeLimit)
330323
viewSize := len(le.adapter.Peers())
331324
for !le.shouldStop() {
332-
time.Sleep(membershipSampleInterval)
325+
time.Sleep(getMembershipSampleInterval())
333326
newSize := len(le.adapter.Peers())
334327
if newSize == viewSize || time.Now().After(endTime) || le.isLeaderExists() {
335328
return
@@ -391,3 +384,39 @@ func (le *leaderElectionSvcImpl) Stop() {
391384
le.stopChan <- struct{}{}
392385
le.stopWG.Wait()
393386
}
387+
388+
func SetStartupGracePeriod(t time.Duration) {
389+
viper.Set("peer.gossip.election.startupGracePeriod", t)
390+
}
391+
392+
func SetMembershipSampleInterval(t time.Duration) {
393+
viper.Set("peer.gossip.election.membershipSampleInterval", t)
394+
}
395+
396+
func SetLeaderAliveThreshold(t time.Duration) {
397+
viper.Set("peer.gossip.election.leaderAliveThreshold", t)
398+
}
399+
400+
func SetLeaderElectionDuration(t time.Duration) {
401+
viper.Set("peer.gossip.election.leaderElectionDuration", t)
402+
}
403+
404+
func getStartupGracePeriod() time.Duration {
405+
return util.GetDurationOrDefault("peer.gossip.election.startupGracePeriod", time.Second*15)
406+
}
407+
408+
func getMembershipSampleInterval() time.Duration {
409+
return util.GetDurationOrDefault("peer.gossip.election.membershipSampleInterval", time.Second)
410+
}
411+
412+
func getLeaderAliveThreshold() time.Duration {
413+
return util.GetDurationOrDefault("peer.gossip.election.leaderAliveThreshold", time.Second*10)
414+
}
415+
416+
func getLeadershipDeclarationInterval() time.Duration {
417+
return time.Duration(getLeaderAliveThreshold() / 2)
418+
}
419+
420+
func getLeaderElectionDuration() time.Duration {
421+
return util.GetDurationOrDefault("peer.gossip.election.leaderElectionDuration", time.Second*5)
422+
}

gossip/election/election_test.go

+55-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import (
2323
"testing"
2424
"time"
2525

26+
"strings"
27+
28+
"github.com/spf13/viper"
2629
"github.com/stretchr/testify/assert"
2730
"github.com/stretchr/testify/mock"
2831
)
@@ -33,11 +36,11 @@ const (
3336
)
3437

3538
func init() {
36-
startupGracePeriod = time.Millisecond * 500
37-
membershipSampleInterval = time.Millisecond * 100
38-
leaderAliveThreshold = time.Millisecond * 500
39-
leadershipDeclarationInterval = leaderAliveThreshold / 2
40-
leaderElectionDuration = time.Millisecond * 500
39+
40+
SetStartupGracePeriod(time.Millisecond * 500)
41+
SetMembershipSampleInterval(time.Millisecond * 100)
42+
SetLeaderAliveThreshold(time.Millisecond * 500)
43+
SetLeaderElectionDuration(time.Millisecond * 500)
4144
}
4245

4346
type msg struct {
@@ -186,7 +189,7 @@ func TestInitPeersAtSameTime(t *testing.T) {
186189
// Scenario: Peers are spawned at the same time
187190
// expected outcome: the peer that has the lowest ID is the leader
188191
peers := createPeers(0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
189-
time.Sleep(startupGracePeriod + leaderElectionDuration)
192+
time.Sleep(getStartupGracePeriod() + getLeaderElectionDuration())
190193
leaders := waitForLeaderElection(t, peers)
191194
isP0leader := peers[len(peers)-1].IsLeader()
192195
assert.True(t, isP0leader, "p0 isn't a leader. Leaders are: %v", leaders)
@@ -198,7 +201,7 @@ func TestInitPeersStartAtIntervals(t *testing.T) {
198201
t.Parallel()
199202
// Scenario: Peers are spawned one by one in a slow rate
200203
// expected outcome: the first peer is the leader although its ID is lowest
201-
peers := createPeers(startupGracePeriod+leadershipDeclarationInterval, 3, 2, 1, 0)
204+
peers := createPeers(getStartupGracePeriod()+getLeadershipDeclarationInterval(), 3, 2, 1, 0)
202205
waitForLeaderElection(t, peers)
203206
assert.True(t, peers[0].IsLeader())
204207
}
@@ -226,9 +229,9 @@ func TestStop(t *testing.T) {
226229
for _, p := range peers {
227230
p.Stop()
228231
}
229-
time.Sleep(leaderAliveThreshold)
232+
time.Sleep(getLeaderAliveThreshold())
230233
gossipCounterAfterStop := atomic.LoadInt32(&gossipCounter)
231-
time.Sleep(leaderAliveThreshold * 5)
234+
time.Sleep(getLeaderAliveThreshold() * 5)
232235
assert.Equal(t, gossipCounterAfterStop, atomic.LoadInt32(&gossipCounter))
233236
}
234237

@@ -265,7 +268,7 @@ func TestConvergence(t *testing.T) {
265268
p.On("Peers").Return(allPeerIds)
266269
}
267270

268-
time.Sleep(leaderAliveThreshold * 5)
271+
time.Sleep(getLeaderAliveThreshold() * 5)
269272
finalLeaders := waitForLeaderElection(t, combinedPeers)
270273
assert.Len(t, finalLeaders, 1, "Combined peer group was suppose to have 1 leader exactly")
271274
assert.Equal(t, leaders1[0], finalLeaders[0], "Combined peer group has different leader than expected:")
@@ -288,12 +291,12 @@ func TestLeadershipTakeover(t *testing.T) {
288291
// Scenario: Peers spawn one by one in descending order.
289292
// After a while, the leader peer stops.
290293
// expected outcome: the peer that takes over is the peer with lowest ID
291-
peers := createPeers(startupGracePeriod+leadershipDeclarationInterval, 5, 4, 3, 2)
294+
peers := createPeers(getStartupGracePeriod()+getLeadershipDeclarationInterval(), 5, 4, 3, 2)
292295
leaders := waitForLeaderElection(t, peers)
293296
assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
294297
assert.Equal(t, "p5", leaders[0])
295298
peers[0].Stop()
296-
time.Sleep(leadershipDeclarationInterval + leaderAliveThreshold*3)
299+
time.Sleep(getLeadershipDeclarationInterval() + getLeaderAliveThreshold()*3)
297300
leaders = waitForLeaderElection(t, peers[1:])
298301
assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
299302
assert.Equal(t, "p2", leaders[0])
@@ -316,7 +319,7 @@ func TestPartition(t *testing.T) {
316319
p.On("Peers").Return([]Peer{})
317320
p.On("Gossip", mock.Anything)
318321
}
319-
time.Sleep(leadershipDeclarationInterval + leaderAliveThreshold*2)
322+
time.Sleep(getLeadershipDeclarationInterval() + getLeaderAliveThreshold()*2)
320323
leaders = waitForMultipleLeadersElection(t, peers, 6)
321324
assert.Len(t, leaders, 6)
322325
for _, p := range peers {
@@ -329,7 +332,7 @@ func TestPartition(t *testing.T) {
329332
p.callbackInvoked = false
330333
p.sharedLock.Unlock()
331334
}
332-
time.Sleep(leadershipDeclarationInterval + leaderAliveThreshold*2)
335+
time.Sleep(getLeadershipDeclarationInterval() + getLeaderAliveThreshold()*2)
333336
leaders = waitForLeaderElection(t, peers)
334337
assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
335338
assert.Equal(t, "p0", leaders[0])
@@ -343,3 +346,41 @@ func TestPartition(t *testing.T) {
343346
}
344347

345348
}
349+
350+
func TestConfigFromFile(t *testing.T) {
351+
preStartupGracePeriod := getStartupGracePeriod()
352+
preMembershipSampleInterval := getMembershipSampleInterval()
353+
preLeaderAliveThreshold := getLeaderAliveThreshold()
354+
preLeaderElectionDuration := getLeaderElectionDuration()
355+
356+
// Recover the config values in order to avoid impacting other tests
357+
defer func() {
358+
SetStartupGracePeriod(preStartupGracePeriod)
359+
SetMembershipSampleInterval(preMembershipSampleInterval)
360+
SetLeaderAliveThreshold(preLeaderAliveThreshold)
361+
SetLeaderElectionDuration(preLeaderElectionDuration)
362+
}()
363+
364+
// Verify if using default values when config is missing
365+
viper.Reset()
366+
assert.Equal(t, time.Second*15, getStartupGracePeriod())
367+
assert.Equal(t, time.Second, getMembershipSampleInterval())
368+
assert.Equal(t, time.Second*10, getLeaderAliveThreshold())
369+
assert.Equal(t, time.Second*5, getLeaderElectionDuration())
370+
assert.Equal(t, getLeaderAliveThreshold()/2, getLeadershipDeclarationInterval())
371+
372+
//Verify reading the values from config file
373+
viper.Reset()
374+
viper.SetConfigName("core")
375+
viper.SetEnvPrefix("CORE")
376+
viper.AddConfigPath("./../../peer")
377+
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
378+
viper.AutomaticEnv()
379+
err := viper.ReadInConfig()
380+
assert.NoError(t, err)
381+
assert.Equal(t, time.Second*15, getStartupGracePeriod())
382+
assert.Equal(t, time.Second, getMembershipSampleInterval())
383+
assert.Equal(t, time.Second*10, getLeaderAliveThreshold())
384+
assert.Equal(t, time.Second*5, getLeaderElectionDuration())
385+
assert.Equal(t, getLeaderAliveThreshold()/2, getLeadershipDeclarationInterval())
386+
}

peer/core.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ peer:
128128
# If this isn't set, the peer will not be known to other organizations.
129129
externalEndpoint:
130130

131+
# Leader election service configuration
132+
election:
133+
# Longest time peer wait for stable membership during leader election startup (unit: second)
134+
startupGracePeriod: 15s
135+
# Interval gossip membership sampled to check its stability (unit: second)
136+
membershipSampleInterval: 1s
137+
# Time pass since last declaration message before peer decide to go to election (unit: second)
138+
leaderAliveThreshold: 10s
139+
# Time between peer sends propose message and declare itself as a leader (sends declaration message) (unit: second)
140+
leaderElectionDuration: 5s
141+
131142
# Sync related configuration
132143
sync:
133144
blocks:

0 commit comments

Comments
 (0)