Skip to content

Commit 2e7d687

Browse files
committed
Refactoring connection between gossip LE and delivery.
While connecting leader election with delivery service there was introduced several redundant entites, this commit takes care to refactor and clean the relevant code. It is crucial to keep this part minimal and compact to allow to maintain reliable connectivty to ordering services. Change-Id: I20737fe116b81d7c535364eb96b8f010b9f50cfe Signed-off-by: Artem Barger <[email protected]>
1 parent 5b59e06 commit 2e7d687

File tree

5 files changed

+88
-63
lines changed

5 files changed

+88
-63
lines changed

gossip/election/election.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package election
1818

1919
import (
2020
"bytes"
21-
"fmt"
2221
"sync"
2322
"sync/atomic"
2423
"time"
@@ -82,7 +81,6 @@ import (
8281
// LeaderElectionAdapter is used by the leader election module
8382
// to send and receive messages and to get membership information
8483
type LeaderElectionAdapter interface {
85-
8684
// Gossip gossips a message to other peers
8785
Gossip(Msg)
8886

@@ -131,7 +129,7 @@ func noopCallback(_ bool) {
131129
// NewLeaderElectionService returns a new LeaderElectionService
132130
func NewLeaderElectionService(adapter LeaderElectionAdapter, id string, callback leadershipCallback) LeaderElectionService {
133131
if len(id) == 0 {
134-
panic(fmt.Errorf("Empty id"))
132+
panic("Empty id")
135133
}
136134
le := &leaderElectionSvcImpl{
137135
id: peerID(id),
@@ -363,13 +361,13 @@ func (le *leaderElectionSvcImpl) IsLeader() bool {
363361
func (le *leaderElectionSvcImpl) beLeader() {
364362
le.logger.Debug(le.id, ": Becoming a leader")
365363
atomic.StoreInt32(&le.isLeader, int32(1))
366-
le.callback(true)
364+
go le.callback(true)
367365
}
368366

369367
func (le *leaderElectionSvcImpl) stopBeingLeader() {
370368
le.logger.Debug(le.id, "Stopped being a leader")
371369
atomic.StoreInt32(&le.isLeader, int32(0))
372-
le.callback(false)
370+
go le.callback(false)
373371
}
374372

375373
func (le *leaderElectionSvcImpl) shouldStop() bool {

gossip/election/election_test.go

+33-18
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ func (m *msg) IsDeclaration() bool {
6262
type peer struct {
6363
mockedMethods map[string]struct{}
6464
mock.Mock
65-
id string
66-
peers map[string]*peer
67-
sharedLock *sync.RWMutex
68-
msgChan chan Msg
69-
isLeaderFromCallback bool
70-
callbackInvoked bool
65+
id string
66+
peers map[string]*peer
67+
sharedLock *sync.RWMutex
68+
msgChan chan Msg
69+
leaderFromCallback bool
70+
callbackInvoked bool
71+
lock sync.RWMutex
7172
LeaderElectionService
7273
}
7374

@@ -131,10 +132,24 @@ func (p *peer) Peers() []Peer {
131132
}
132133

133134
func (p *peer) leaderCallback(isLeader bool) {
134-
p.isLeaderFromCallback = isLeader
135+
p.lock.Lock()
136+
defer p.lock.Unlock()
137+
p.leaderFromCallback = isLeader
135138
p.callbackInvoked = true
136139
}
137140

141+
func (p *peer) isLeaderFromCallback() bool {
142+
p.lock.RLock()
143+
defer p.lock.RUnlock()
144+
return p.leaderFromCallback
145+
}
146+
147+
func (p *peer) isCallbackInvoked() bool {
148+
p.lock.RLock()
149+
defer p.lock.RUnlock()
150+
return p.callbackInvoked
151+
}
152+
138153
func createPeers(spawnInterval time.Duration, ids ...int) []*peer {
139154
peers := make([]*peer, len(ids))
140155
peerMap := make(map[string]*peer)
@@ -152,7 +167,7 @@ func createPeers(spawnInterval time.Duration, ids ...int) []*peer {
152167
func createPeer(id int, peerMap map[string]*peer, l *sync.RWMutex) *peer {
153168
idStr := fmt.Sprintf("p%d", id)
154169
c := make(chan Msg, 100)
155-
p := &peer{id: idStr, peers: peerMap, sharedLock: l, msgChan: c, mockedMethods: make(map[string]struct{}), isLeaderFromCallback: false, callbackInvoked: false}
170+
p := &peer{id: idStr, peers: peerMap, sharedLock: l, msgChan: c, mockedMethods: make(map[string]struct{}), leaderFromCallback: false, callbackInvoked: false}
156171
p.LeaderElectionService = NewLeaderElectionService(p, idStr, p.leaderCallback)
157172
l.Lock()
158173
peerMap[idStr] = p
@@ -192,7 +207,7 @@ func TestInitPeersAtSameTime(t *testing.T) {
192207
leaders := waitForLeaderElection(t, peers)
193208
isP0leader := peers[len(peers)-1].IsLeader()
194209
assert.True(t, isP0leader, "p0 isn't a leader. Leaders are: %v", leaders)
195-
assert.True(t, peers[len(peers)-1].isLeaderFromCallback, "p0 didn't got leaderhip change callback invoked")
210+
assert.True(t, peers[len(peers)-1].isLeaderFromCallback(), "p0 didn't got leaderhip change callback invoked")
196211
assert.Len(t, leaders, 1, "More than 1 leader elected")
197212
}
198213

@@ -274,12 +289,12 @@ func TestConvergence(t *testing.T) {
274289

275290
for _, p := range combinedPeers {
276291
if p.id == finalLeaders[0] {
277-
assert.True(t, p.isLeaderFromCallback, "Leadership callback result is wrong for ", p.id)
278-
assert.True(t, p.callbackInvoked, "Leadership callback wasn't invoked for ", p.id)
292+
assert.True(t, p.isLeaderFromCallback(), "Leadership callback result is wrong for ", p.id)
293+
assert.True(t, p.isCallbackInvoked(), "Leadership callback wasn't invoked for ", p.id)
279294
} else {
280-
assert.False(t, p.isLeaderFromCallback, "Leadership callback result is wrong for ", p.id)
295+
assert.False(t, p.isLeaderFromCallback(), "Leadership callback result is wrong for ", p.id)
281296
if p.id == leaders2[0] {
282-
assert.True(t, p.callbackInvoked, "Leadership callback wasn't invoked for ", p.id)
297+
assert.True(t, p.isCallbackInvoked(), "Leadership callback wasn't invoked for ", p.id)
283298
}
284299
}
285300
}
@@ -312,7 +327,7 @@ func TestPartition(t *testing.T) {
312327
leaders := waitForLeaderElection(t, peers)
313328
assert.Len(t, leaders, 1, "Only 1 leader should have been elected")
314329
assert.Equal(t, "p0", leaders[0])
315-
assert.True(t, peers[len(peers)-1].isLeaderFromCallback, "Leadership callback result is wrong for %s", peers[len(peers)-1].id)
330+
assert.True(t, peers[len(peers)-1].isLeaderFromCallback(), "Leadership callback result is wrong for %s", peers[len(peers)-1].id)
316331

317332
for _, p := range peers {
318333
p.On("Peers").Return([]Peer{})
@@ -322,7 +337,7 @@ func TestPartition(t *testing.T) {
322337
leaders = waitForMultipleLeadersElection(t, peers, 6)
323338
assert.Len(t, leaders, 6)
324339
for _, p := range peers {
325-
assert.True(t, p.isLeaderFromCallback, "Leadership callback result is wrong for %s", p.id)
340+
assert.True(t, p.isLeaderFromCallback(), "Leadership callback result is wrong for %s", p.id)
326341
}
327342

328343
for _, p := range peers {
@@ -337,10 +352,10 @@ func TestPartition(t *testing.T) {
337352
assert.Equal(t, "p0", leaders[0])
338353
for _, p := range peers {
339354
if p.id == leaders[0] {
340-
assert.True(t, p.isLeaderFromCallback, "Leadership callback result is wrong for %s", p.id)
355+
assert.True(t, p.isLeaderFromCallback(), "Leadership callback result is wrong for %s", p.id)
341356
} else {
342-
assert.False(t, p.isLeaderFromCallback, "Leadership callback result is wrong for %s", p.id)
343-
assert.True(t, p.callbackInvoked, "Leadership callback wasn't invoked for %s", p.id)
357+
assert.False(t, p.isLeaderFromCallback(), "Leadership callback result is wrong for %s", p.id)
358+
assert.True(t, p.isCallbackInvoked(), "Leadership callback wasn't invoked for %s", p.id)
344359
}
345360
}
346361

gossip/service/gossip_service.go

+35-34
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
peerComm "github.com/hyperledger/fabric/core/comm"
2323
"github.com/hyperledger/fabric/core/committer"
2424
"github.com/hyperledger/fabric/core/deliverservice"
25+
"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
2526
"github.com/hyperledger/fabric/gossip/api"
2627
gossipCommon "github.com/hyperledger/fabric/gossip/common"
2728
"github.com/hyperledger/fabric/gossip/election"
@@ -186,23 +187,26 @@ func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committe
186187
}
187188
}
188189

190+
// Delivery service might be nil only if it was not able to get connected
191+
// to the ordering service
189192
if g.deliveryService != nil {
193+
// Parameters:
194+
// - peer.gossip.useLeaderElection
195+
// - peer.gossip.orgLeader
196+
//
197+
// are mutual exclusive, setting both to true is not defined, hence
198+
// peer will panic and terminate
190199
leaderElection := viper.GetBool("peer.gossip.useLeaderElection")
191-
staticOrderConnection := viper.GetBool("peer.gossip.orgLeader")
200+
isStaticOrgLeader := viper.GetBool("peer.gossip.orgLeader")
192201

193-
if leaderElection && staticOrderConnection {
194-
msg := "Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution"
195-
logger.Panic(msg)
196-
} else if leaderElection {
202+
if leaderElection && isStaticOrgLeader {
203+
logger.Panic("Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution")
204+
}
205+
206+
if leaderElection {
197207
logger.Debug("Delivery uses dynamic leader election mechanism, channel", chainID)
198-
connector := &leaderElectionDeliverConnector{
199-
deliverer: g.deliveryService,
200-
committer: committer,
201-
chainID: chainID,
202-
}
203-
electionService := g.newLeaderElectionComponent(gossipCommon.ChainID(connector.chainID), connector.leadershipStatusChange)
204-
g.leaderElection[chainID] = electionService
205-
} else if staticOrderConnection {
208+
g.leaderElection[chainID] = g.newLeaderElectionComponent(chainID, g.onStatusChangeFactory(chainID, committer))
209+
} else if isStaticOrgLeader {
206210
logger.Debug("This peer is configured to connect to ordering service for blocks delivery, channel", chainID)
207211
g.deliveryService.StartDeliverForChannel(chainID, committer)
208212
} else {
@@ -270,9 +274,9 @@ func (g *gossipServiceImpl) Stop() {
270274
}
271275
}
272276

273-
func (g *gossipServiceImpl) newLeaderElectionComponent(channel gossipCommon.ChainID, callback func(bool)) election.LeaderElectionService {
277+
func (g *gossipServiceImpl) newLeaderElectionComponent(chainID string, callback func(bool)) election.LeaderElectionService {
274278
PKIid := g.idMapper.GetPKIidOfCert(g.peerIdentity)
275-
adapter := election.NewAdapter(g, PKIid, channel)
279+
adapter := election.NewAdapter(g, PKIid, gossipCommon.ChainID(chainID))
276280
return election.NewLeaderElectionService(adapter, string(PKIid), callback)
277281
}
278282

@@ -285,6 +289,22 @@ func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool {
285289
return false
286290
}
287291

292+
func (g *gossipServiceImpl) onStatusChangeFactory(chainID string, committer blocksprovider.LedgerInfo) func(bool) {
293+
return func(isLeader bool) {
294+
if isLeader {
295+
if err := g.deliveryService.StartDeliverForChannel(chainID, committer); err != nil {
296+
logger.Error("Delivery service is not able to start blocks delivery for chain, due to", err)
297+
}
298+
} else {
299+
if err := g.deliveryService.StopDeliverForChannel(chainID); err != nil {
300+
logger.Error("Delivery service is not able to stop blocks delivery for chain, due to", err)
301+
}
302+
303+
}
304+
305+
}
306+
}
307+
288308
func orgListFromConfig(config Config) []string {
289309
var orgList []string
290310
for orgName := range config.Organizations() {
@@ -324,22 +344,3 @@ func (s *secImpl) VerifyByChannel(chainID gossipCommon.ChainID, peerIdentity api
324344
func (s *secImpl) ValidateIdentity(peerIdentity api.PeerIdentityType) error {
325345
return nil
326346
}
327-
328-
type leaderElectionDeliverConnector struct {
329-
deliverer deliverclient.DeliverService
330-
chainID string
331-
committer committer.Committer
332-
}
333-
334-
func (ledc *leaderElectionDeliverConnector) leadershipStatusChange(isLeader bool) {
335-
if isLeader {
336-
if err := ledc.deliverer.StartDeliverForChannel(ledc.chainID, ledc.committer); err != nil {
337-
logger.Error("Delivery service is not able to start blocks delivery for chain, due to", err)
338-
}
339-
} else {
340-
if err := ledc.deliverer.StopDeliverForChannel(ledc.chainID); err != nil {
341-
logger.Error("Delivery service is not able to stop blocks delivery for chain, due to", err)
342-
}
343-
344-
}
345-
}

gossip/service/gossip_service_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ func TestLeaderElectionWithRealGossip(t *testing.T) {
347347

348348
for i := 0; i < n; i++ {
349349
services[i] = &electionService{nil, false, 0}
350-
services[i].LeaderElectionService = gossips[i].(*gossipServiceImpl).newLeaderElectionComponent(gossipCommon.ChainID(channelName), services[i].callback)
350+
services[i].LeaderElectionService = gossips[i].(*gossipServiceImpl).newLeaderElectionComponent(channelName, services[i].callback)
351351
}
352352

353353
logger.Warning("Waiting for leader election")
@@ -373,7 +373,7 @@ func TestLeaderElectionWithRealGossip(t *testing.T) {
373373

374374
for idx, i := range secondChannelPeerIndexes {
375375
secondChannelServices[idx] = &electionService{nil, false, 0}
376-
secondChannelServices[idx].LeaderElectionService = gossips[i].(*gossipServiceImpl).newLeaderElectionComponent(gossipCommon.ChainID(secondChannelName), secondChannelServices[idx].callback)
376+
secondChannelServices[idx].LeaderElectionService = gossips[i].(*gossipServiceImpl).newLeaderElectionComponent(secondChannelName, secondChannelServices[idx].callback)
377377
}
378378

379379
assert.True(t, waitForLeaderElection(t, secondChannelServices, time.Second*30, time.Second*2), "One leader should be selected for chanB")

peer/core.yaml

+15-4
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,24 @@ peer:
6868

6969
# Gossip related configuration
7070
gossip:
71+
# Bootstrap set to initialize gossip with
7172
bootstrap: 127.0.0.1:7051
72-
# Use automatically chosen peer (high avalibility) to distribute blocks in channel or static one
73-
# Setting this true and orgLeader true cause panic exit
73+
74+
# NOTE: orgLeader and useLeaderElection parameters are mutual exclusive
75+
# setting both to true would result in the termination of the peer, since this is undefined
76+
# state.
77+
78+
# Defines whenever peer will initialize dynamic algorithm for
79+
# "leader" selection, where leader is the peer to establish
80+
# connection with ordering service and use delivery protocol
81+
# to pull ledger blocks from ordering service
7482
useLeaderElection: false
75-
# For debug - is peer is its org leader and should pass blocks from orderer to other peers in org
76-
# Works only if useLeaderElection set to false
83+
# Statically defines peer to be an organization "leader",
84+
# where this means that current peer will maintain connection
85+
# with ordering service and disseminate block across peers in
86+
# its own organization
7787
orgLeader: true
88+
7889
# ID of this instance
7990
endpoint:
8091
# Maximum count of blocks we store in memory

0 commit comments

Comments
 (0)