Skip to content

Commit 5eb5d07

Browse files
[FAB-2780] Expire leadership, stateInfo and data
Taking care of leadership messages store, state info cache and blocks message store in channel Change-Id: I9122fd7569ad156b248b269d8ab4016b7e22d176 Signed-off-by: Gennady Laventman <[email protected]>
1 parent 9d12166 commit 5eb5d07

File tree

5 files changed

+206
-26
lines changed

5 files changed

+206
-26
lines changed

gossip/election/election.go

+4
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,7 @@ func getLeadershipDeclarationInterval() time.Duration {
418418
func getLeaderElectionDuration() time.Duration {
419419
return util.GetDurationOrDefault("peer.gossip.election.leaderElectionDuration", time.Second*5)
420420
}
421+
422+
func GetMsgExpirationTimeout() time.Duration {
423+
return getLeaderAliveThreshold() * 10
424+
}

gossip/gossip/channel/channel.go

+43-12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/hyperledger/fabric/gossip/comm"
2929
"github.com/hyperledger/fabric/gossip/common"
3030
"github.com/hyperledger/fabric/gossip/discovery"
31+
"github.com/hyperledger/fabric/gossip/election"
3132
"github.com/hyperledger/fabric/gossip/filter"
3233
"github.com/hyperledger/fabric/gossip/gossip/msgstore"
3334
"github.com/hyperledger/fabric/gossip/gossip/pull"
@@ -45,6 +46,9 @@ type Config struct {
4546
PullPeerNum int
4647
PullInterval time.Duration
4748
RequestStateInfoInterval time.Duration
49+
50+
BlockExpirationInterval time.Duration
51+
StateInfoExpirationInterval time.Duration
4852
}
4953

5054
// GossipChannel defines an object that deals with all channel-related messages
@@ -166,13 +170,22 @@ func NewGossipChannel(pkiID common.PKIidType, mcs api.MessageCryptoService, chai
166170
gc.memFilter = &membershipFilter{adapter: gc.Adapter, gossipChannel: gc}
167171

168172
comparator := proto.NewGossipMessageComparator(adapter.GetConf().MaxBlockCountToStore)
169-
gc.blockMsgStore = msgstore.NewMessageStore(comparator, func(m interface{}) {
173+
174+
gc.blocksPuller = gc.createBlockPuller()
175+
176+
gc.blockMsgStore = msgstore.NewMessageStoreExpirable(comparator, func(m interface{}) {
177+
gc.blocksPuller.Remove(m.(*proto.SignedGossipMessage))
178+
}, gc.GetConf().BlockExpirationInterval, nil, nil, func(m interface{}) {
170179
gc.blocksPuller.Remove(m.(*proto.SignedGossipMessage))
171180
})
172181

173-
gc.stateInfoMsgStore = newStateInfoCache()
174-
gc.blocksPuller = gc.createBlockPuller()
175-
gc.leaderMsgStore = msgstore.NewMessageStore(proto.NewGossipMessageComparator(0), func(m interface{}) {})
182+
gc.stateInfoMsgStore = newStateInfoCache(gc.GetConf().StateInfoExpirationInterval)
183+
184+
ttl := election.GetMsgExpirationTimeout()
185+
noopFunc := func(m interface{}) {}
186+
pol := proto.NewGossipMessageComparator(0)
187+
188+
gc.leaderMsgStore = msgstore.NewMessageStoreExpirable(pol, noopFunc, ttl, nil, nil, nil)
176189

177190
gc.ConfigureChannel(joinMsg)
178191

@@ -189,6 +202,9 @@ func (gc *gossipChannel) Stop() {
189202
gc.blocksPuller.Stop()
190203
gc.stateInfoPublishScheduler.Stop()
191204
gc.stateInfoRequestScheduler.Stop()
205+
gc.leaderMsgStore.Stop()
206+
gc.stateInfoMsgStore.Stop()
207+
gc.blockMsgStore.Stop()
192208
}
193209

194210
func (gc *gossipChannel) periodicalInvocation(fn func(), c <-chan time.Time) {
@@ -586,16 +602,31 @@ func (gc *gossipChannel) UpdateStateInfo(msg *proto.SignedGossipMessage) {
586602
atomic.StoreInt32(&gc.shouldGossipStateInfo, int32(1))
587603
}
588604

589-
// NewStateInfoMessageStore returns a MessageStore
590-
func NewStateInfoMessageStore() msgstore.MessageStore {
591-
return msgstore.NewMessageStore(proto.NewGossipMessageComparator(0), func(m interface{}) {})
605+
// NewStateInfoMessageStore returns a expirable MessageStore
606+
// ttl is time duration before msg expires and removed from store
607+
func NewStateInfoMessageStore(ttl time.Duration) msgstore.MessageStore {
608+
return NewStateInfoMessageStoreWithCallback(ttl, nil)
592609
}
593610

594-
func newStateInfoCache() *stateInfoCache {
595-
return &stateInfoCache{
596-
MembershipStore: util.NewMembershipStore(),
597-
MessageStore: NewStateInfoMessageStore(),
611+
// NewStateInfoMessageStoreWithCallback returns a exiprable MessageStore
612+
// Callback invoked once message expires and removed from store
613+
// ttl is time duration before msg expires
614+
func NewStateInfoMessageStoreWithCallback(ttl time.Duration, callback func(m interface{})) msgstore.MessageStore {
615+
pol := proto.NewGossipMessageComparator(0)
616+
noopTrigger := func(m interface{}) {}
617+
return msgstore.NewMessageStoreExpirable(pol, noopTrigger, ttl, nil, nil, callback)
618+
}
619+
620+
func newStateInfoCache(ttl time.Duration) *stateInfoCache {
621+
membershipStore := util.NewMembershipStore()
622+
callback := func(m interface{}) {
623+
membershipStore.Remove(m.(*proto.SignedGossipMessage).GetStateInfo().PkiId)
624+
}
625+
s := &stateInfoCache{
626+
MembershipStore: membershipStore,
627+
MessageStore: NewStateInfoMessageStoreWithCallback(ttl, callback),
598628
}
629+
return s
599630
}
600631

601632
// stateInfoCache is actually a messageStore
@@ -611,8 +642,8 @@ type stateInfoCache struct {
611642
// Message must be a StateInfo message.
612643
func (cache *stateInfoCache) Add(msg *proto.SignedGossipMessage) bool {
613644
added := cache.MessageStore.Add(msg)
614-
pkiID := msg.GetStateInfo().PkiId
615645
if added {
646+
pkiID := msg.GetStateInfo().PkiId
616647
cache.MembershipStore.Put(pkiID, msg)
617648
}
618649
return added

gossip/gossip/channel/channel_test.go

+147-7
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ type msgMutator func(message *proto.Envelope)
4040

4141
var conf = Config{
4242
ID: "test",
43-
PublishStateInfoInterval: time.Millisecond * 100,
44-
MaxBlockCountToStore: 100,
45-
PullPeerNum: 3,
46-
PullInterval: time.Second,
47-
RequestStateInfoInterval: time.Millisecond * 100,
43+
PublishStateInfoInterval: time.Millisecond * 100,
44+
MaxBlockCountToStore: 100,
45+
PullPeerNum: 3,
46+
PullInterval: time.Second,
47+
RequestStateInfoInterval: time.Millisecond * 100,
48+
BlockExpirationInterval: time.Second * 6,
49+
StateInfoExpirationInterval: time.Second * 6,
4850
}
4951

5052
func init() {
@@ -275,7 +277,8 @@ func TestChannelPeriodicalPublishStateInfo(t *testing.T) {
275277
})
276278

277279
gc := NewGossipChannel(pkiIDInOrg1, cs, channelA, adapter, &joinChanMsg{})
278-
gc.UpdateStateInfo(createStateInfoMsg(ledgerHeight, pkiIDInOrg1, channelA))
280+
stateInfoMsg := createStateInfoMsg(ledgerHeight, pkiIDInOrg1, channelA)
281+
gc.UpdateStateInfo(stateInfoMsg)
279282

280283
var msg *proto.SignedGossipMessage
281284
select {
@@ -289,6 +292,14 @@ func TestChannelPeriodicalPublishStateInfo(t *testing.T) {
289292
height, err := strconv.ParseInt(string(md), 10, 64)
290293
assert.NoError(t, err, "ReceivedMetadata is invalid")
291294
assert.Equal(t, ledgerHeight, int(height), "Received different ledger height than expected")
295+
296+
// We will not update StateInfo in store, so store will become empty
297+
time.Sleep(conf.StateInfoExpirationInterval + time.Second)
298+
//Store is empty
299+
assert.Equal(t, 0, gc.(*gossipChannel).stateInfoMsgStore.MessageStore.Size(), "StateInfo MessageStore should be empty")
300+
assert.Equal(t, 0, gc.(*gossipChannel).stateInfoMsgStore.MembershipStore.Size(), "StateInfo MembershipStore should be empty")
301+
302+
gc.Stop()
292303
}
293304

294305
func TestChannelPull(t *testing.T) {
@@ -523,6 +534,99 @@ func TestChannelAddToMessageStore(t *testing.T) {
523534
assert.True(t, gc.EligibleForChannel(discovery.NetworkMember{PKIid: pkiIDInOrg1}))
524535
}
525536

537+
func TestChannelAddToMessageStoreExpire(t *testing.T) {
538+
t.Parallel()
539+
540+
cs := &cryptoService{}
541+
cs.On("VerifyBlock", mock.Anything).Return(nil)
542+
demuxedMsgs := make(chan *proto.SignedGossipMessage, 1)
543+
adapter := new(gossipAdapterMock)
544+
configureAdapter(adapter)
545+
gc := NewGossipChannel(pkiIDInOrg1, cs, channelA, adapter, &joinChanMsg{})
546+
adapter.On("Gossip", mock.Anything)
547+
adapter.On("Send", mock.Anything, mock.Anything)
548+
adapter.On("DeMultiplex", mock.Anything).Run(func(arg mock.Arguments) {
549+
demuxedMsgs <- arg.Get(0).(*proto.SignedGossipMessage)
550+
})
551+
552+
respondedChan := make(chan *proto.GossipMessage, 1)
553+
messageRelayer := func(arg mock.Arguments) {
554+
msg := arg.Get(0).(*proto.GossipMessage)
555+
respondedChan <- msg
556+
}
557+
558+
// We make sure that if we get a new message it is de-multiplexed,
559+
gc.HandleMessage(&receivedMsg{msg: dataMsgOfChannel(11, channelA), PKIID: pkiIDInOrg1})
560+
select {
561+
case <-time.After(time.Second):
562+
t.Fatal("Haven't detected a demultiplexing within a time period")
563+
case <-demuxedMsgs:
564+
}
565+
566+
// Lets check digests and state info store
567+
stateInfoMsg := createStateInfoMsg(10, pkiIDInOrg1, channelA)
568+
gc.AddToMsgStore(stateInfoMsg)
569+
helloMsg := createHelloMsg(pkiIDInOrg1)
570+
helloMsg.On("Respond", mock.Anything).Run(messageRelayer)
571+
gc.HandleMessage(helloMsg)
572+
select {
573+
case <-time.After(time.Second):
574+
t.Fatal("Haven't responded to hello message within a time period")
575+
case msg := <-respondedChan:
576+
if msg.IsDigestMsg() {
577+
assert.Equal(t, 1, len(msg.GetDataDig().Digests), "Number of digests returned by channel blockPuller incorrect")
578+
} else {
579+
t.Fatal("Not correct pull msg type in responce - expect digest")
580+
}
581+
}
582+
583+
time.Sleep(gc.(*gossipChannel).GetConf().BlockExpirationInterval + time.Second)
584+
585+
// message expired in store, but still isn't demultiplexed when we
586+
// receive that message again
587+
gc.HandleMessage(&receivedMsg{msg: dataMsgOfChannel(11, channelA), PKIID: pkiIDInOrg1})
588+
select {
589+
case <-time.After(time.Second):
590+
case <-demuxedMsgs:
591+
t.Fatal("Demultiplexing detected, even though it wasn't supposed to happen")
592+
}
593+
594+
// Lets check digests and state info store - state info expired, its add will do nothing and digest should not be sent
595+
gc.AddToMsgStore(stateInfoMsg)
596+
gc.HandleMessage(helloMsg)
597+
select {
598+
case <-time.After(time.Second):
599+
case <-respondedChan:
600+
t.Fatal("No digest should be sent")
601+
}
602+
603+
time.Sleep(gc.(*gossipChannel).GetConf().BlockExpirationInterval + time.Second)
604+
// message removed from store, so it will be demultiplexed when we
605+
// receive that message again
606+
gc.HandleMessage(&receivedMsg{msg: dataMsgOfChannel(11, channelA), PKIID: pkiIDInOrg1})
607+
select {
608+
case <-time.After(time.Second):
609+
t.Fatal("Haven't detected a demultiplexing within a time period")
610+
case <-demuxedMsgs:
611+
}
612+
613+
// Lets check digests and state info store - state info removed as well, so it will be added back and digest will be created
614+
gc.AddToMsgStore(stateInfoMsg)
615+
gc.HandleMessage(helloMsg)
616+
select {
617+
case <-time.After(time.Second):
618+
t.Fatal("Haven't responded to hello message within a time period")
619+
case msg := <-respondedChan:
620+
if msg.IsDigestMsg() {
621+
assert.Equal(t, 1, len(msg.GetDataDig().Digests), "Number of digests returned by channel blockPuller incorrect")
622+
} else {
623+
t.Fatal("Not correct pull msg type in responce - expect digest")
624+
}
625+
}
626+
627+
gc.Stop()
628+
}
629+
526630
func TestChannelBadBlocks(t *testing.T) {
527631
t.Parallel()
528632
receivedMessages := make(chan *proto.SignedGossipMessage, 1)
@@ -693,7 +797,8 @@ func TestChannelStateInfoSnapshot(t *testing.T) {
693797
gc.HandleMessage(&receivedMsg{PKIID: pkiIDInOrg1, msg: stateInfoSnapshotForChannel(channelA, createStateInfoMsg(4, pkiIDInOrg1, channelA))})
694798

695799
// Ensure we process stateInfo snapshots that are OK
696-
gc.HandleMessage(&receivedMsg{PKIID: pkiIDInOrg1, msg: stateInfoSnapshotForChannel(channelA, createStateInfoMsg(4, pkiIDInOrg1, channelA))})
800+
stateInfoMsg := &receivedMsg{PKIID: pkiIDInOrg1, msg: stateInfoSnapshotForChannel(channelA, createStateInfoMsg(4, pkiIDInOrg1, channelA))}
801+
gc.HandleMessage(stateInfoMsg)
697802
assert.NotEmpty(t, gc.GetPeers())
698803
assert.Equal(t, "4", string(gc.GetPeers()[0].Metadata))
699804

@@ -757,6 +862,41 @@ func TestChannelStateInfoSnapshot(t *testing.T) {
757862
invalidStateInfoSnapshot = stateInfoSnapshotForChannel(channelA, createStateInfoMsg(4, common.PKIidType("unknown"), channelA))
758863
gc.HandleMessage(&receivedMsg{PKIID: pkiIDInOrg1, msg: invalidStateInfoSnapshot})
759864

865+
// Lets expire msg in store
866+
time.Sleep(gc.(*gossipChannel).GetConf().StateInfoExpirationInterval + time.Second)
867+
868+
// Lets check is state info store can't add expired msg but appear as empty to outside world
869+
gc.HandleMessage(stateInfoMsg)
870+
assert.Empty(t, gc.GetPeers())
871+
// Lets see if snapshot now empty, after message in store expired
872+
go gc.HandleMessage(snapshotReq)
873+
select {
874+
case <-time.After(time.Second):
875+
t.Fatal("Haven't received a state info snapshot on time")
876+
case msg := <-sentMessages:
877+
elements := msg.GetStateSnapshot().Elements
878+
assert.Len(t, elements, 0, "StateInfo snapshot should contain zero messages")
879+
}
880+
881+
// Lets make sure msg removed from store
882+
time.Sleep(gc.(*gossipChannel).GetConf().StateInfoExpirationInterval + time.Second)
883+
884+
// Lets check is state info store add just expired msg
885+
gc.HandleMessage(stateInfoMsg)
886+
assert.NotEmpty(t, gc.GetPeers())
887+
// Lets see if snapshot is not empty now, after message was added back to store
888+
go gc.HandleMessage(snapshotReq)
889+
select {
890+
case <-time.After(time.Second):
891+
t.Fatal("Haven't received a state info snapshot on time")
892+
case msg := <-sentMessages:
893+
elements := msg.GetStateSnapshot().Elements
894+
assert.Len(t, elements, 1)
895+
sMsg, err := elements[0].ToGossipMessage()
896+
assert.NoError(t, err)
897+
assert.Equal(t, []byte("4"), sMsg.GetStateInfo().Metadata)
898+
}
899+
760900
}
761901

762902
func TestChannelStop(t *testing.T) {

gossip/gossip/chanstate.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,14 @@ type gossipAdapterImpl struct {
126126

127127
func (ga *gossipAdapterImpl) GetConf() channel.Config {
128128
return channel.Config{
129-
ID: ga.conf.ID,
130-
MaxBlockCountToStore: ga.conf.MaxBlockCountToStore,
131-
PublishStateInfoInterval: ga.conf.PublishStateInfoInterval,
132-
PullInterval: ga.conf.PullInterval,
133-
PullPeerNum: ga.conf.PullPeerNum,
134-
RequestStateInfoInterval: ga.conf.RequestStateInfoInterval,
129+
ID: ga.conf.ID,
130+
MaxBlockCountToStore: ga.conf.MaxBlockCountToStore,
131+
PublishStateInfoInterval: ga.conf.PublishStateInfoInterval,
132+
PullInterval: ga.conf.PullInterval,
133+
PullPeerNum: ga.conf.PullPeerNum,
134+
RequestStateInfoInterval: ga.conf.RequestStateInfoInterval,
135+
BlockExpirationInterval: ga.conf.PullInterval * 100,
136+
StateInfoExpirationInterval: ga.conf.PublishStateInfoInterval * 100,
135137
}
136138
}
137139

gossip/gossip/gossip_impl.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,10 @@ func NewGossipService(conf *Config, s *grpc.Server, secAdvisor api.SecurityAdvis
9090
return nil
9191
}
9292

93+
stateInfoExpirationInterval := conf.PublishStateInfoInterval * 100
94+
9395
g := &gossipServiceImpl{
94-
stateInfoMsgStore: channel.NewStateInfoMessageStore(),
96+
stateInfoMsgStore: channel.NewStateInfoMessageStore(stateInfoExpirationInterval),
9597
selfOrg: secAdvisor.OrgByPeerIdentity(selfIdentity),
9698
secAdvisor: secAdvisor,
9799
selfIdentity: selfIdentity,
@@ -641,6 +643,7 @@ func (g *gossipServiceImpl) Stop() {
641643
g.toDieChan <- struct{}{}
642644
g.emitter.Stop()
643645
g.ChannelDeMultiplexer.Close()
646+
g.stateInfoMsgStore.Stop()
644647
g.stopSignal.Wait()
645648
comWG.Wait()
646649
}

0 commit comments

Comments
 (0)