Skip to content

Commit 1ba3dbc

Browse files
committed
FAB-14616 externalize gossip state transfer config
Externalize gossip state transfer parameters so it could be controlled via peer configuration. Change-Id: Ie5b26941d5e97c605c704298e97eed5ed178f0ca Signed-off-by: Artem Barger <bartem@il.ibm.com>
1 parent e43bdd0 commit 1ba3dbc

File tree

3 files changed

+107
-18
lines changed

3 files changed

+107
-18
lines changed

gossip/state/state.go

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/hyperledger/fabric/protos/transientstore"
2828
"github.com/hyperledger/fabric/protoutil"
2929
"github.com/pkg/errors"
30+
"github.com/spf13/viper"
3031
)
3132

3233
// GossipStateProvider is the interface to acquire sequences of the ledger blocks
@@ -55,6 +56,17 @@ const (
5556
enqueueRetryInterval = time.Millisecond * 100
5657
)
5758

59+
// Configuration keeps state transfer configuration parameters
60+
type Configuration struct {
61+
AntiEntropyInterval time.Duration
62+
AntiEntropyStateResponseTimeout time.Duration
63+
AntiEntropyBatchSize uint64
64+
MaxBlockDistance int
65+
AntiEntropyMaxRetries int
66+
ChannelBufferSize int
67+
EnableStateTransfer bool
68+
}
69+
5870
// GossipAdapter defines gossip/communication required interface for state provider
5971
type GossipAdapter interface {
6072
// Send sends a message to remote peers
@@ -155,6 +167,8 @@ type GossipStateProviderImpl struct {
155167
requestValidator *stateRequestValidator
156168

157169
blockingMode bool
170+
171+
config *Configuration
158172
}
159173

160174
var logger = util.GetLogger(util.StateLogger, "")
@@ -164,18 +178,61 @@ type stateRequestValidator struct {
164178
}
165179

166180
// validate checks for RemoteStateRequest message validity
167-
func (v *stateRequestValidator) validate(request *proto.RemoteStateRequest) error {
181+
func (v *stateRequestValidator) validate(request *proto.RemoteStateRequest, batchSize uint64) error {
168182
if request.StartSeqNum > request.EndSeqNum {
169183
return errors.Errorf("Invalid sequence interval [%d...%d).", request.StartSeqNum, request.EndSeqNum)
170184
}
171185

172-
if request.EndSeqNum > defAntiEntropyBatchSize+request.StartSeqNum {
186+
if request.EndSeqNum > batchSize+request.StartSeqNum {
173187
return errors.Errorf("Requesting blocks range [%d-%d) greater than configured allowed"+
174-
" (%d) batching size for anti-entropy.", request.StartSeqNum, request.EndSeqNum, defAntiEntropyBatchSize)
188+
" (%d) batching size for anti-entropy.", request.StartSeqNum, request.EndSeqNum, batchSize)
175189
}
176190
return nil
177191
}
178192

193+
// readConfiguration reading state configuration
194+
func readConfiguration() *Configuration {
195+
config := &Configuration{
196+
AntiEntropyInterval: defAntiEntropyInterval,
197+
AntiEntropyStateResponseTimeout: defAntiEntropyStateResponseTimeout,
198+
AntiEntropyBatchSize: defAntiEntropyBatchSize,
199+
MaxBlockDistance: defMaxBlockDistance,
200+
AntiEntropyMaxRetries: defAntiEntropyMaxRetries,
201+
ChannelBufferSize: defChannelBufferSize,
202+
EnableStateTransfer: true,
203+
}
204+
205+
if viper.IsSet("peer.gossip.state.checkInterval") {
206+
config.AntiEntropyInterval = viper.GetDuration("peer.gossip.state.checkInterval")
207+
}
208+
209+
if viper.IsSet("peer.gossip.state.responseTimeout") {
210+
config.AntiEntropyStateResponseTimeout = viper.GetDuration("peer.gossip.state.responseTimeout")
211+
}
212+
213+
if viper.IsSet("peer.gossip.state.batchSize") {
214+
config.AntiEntropyBatchSize = uint64(viper.GetInt("peer.gossip.state.batchSize"))
215+
}
216+
217+
if viper.IsSet("peer.gossip.state.blockBufferSize") {
218+
config.MaxBlockDistance = viper.GetInt("peer.gossip.state.blockBufferSize")
219+
}
220+
221+
if viper.IsSet("peer.gossip.state.maxRetries") {
222+
config.AntiEntropyMaxRetries = viper.GetInt("peer.gossip.state.maxRetries")
223+
}
224+
225+
if viper.IsSet("peer.gossip.state.channelSize") {
226+
config.ChannelBufferSize = viper.GetInt("peer.gossip.state.channelSize")
227+
}
228+
229+
if viper.IsSet("peer.gossip.state.enabled") {
230+
config.EnableStateTransfer = viper.GetBool("peer.gossip.state.enabled")
231+
}
232+
233+
return config
234+
}
235+
179236
// NewGossipStateProvider creates state provider with coordinator instance
180237
// to orchestrate arrival of private rwsets and blocks before committing them into the ledger.
181238
func NewGossipStateProvider(chainID string, services *ServicesMediator, ledger ledgerResources,
@@ -223,6 +280,9 @@ func NewGossipStateProvider(chainID string, services *ServicesMediator, ledger l
223280
return nil
224281
}
225282

283+
// Reading state configuration
284+
config := readConfiguration()
285+
226286
s := &GossipStateProviderImpl{
227287
// MessageCryptoService
228288
mediator: services,
@@ -245,9 +305,9 @@ func NewGossipStateProvider(chainID string, services *ServicesMediator, ledger l
245305

246306
ledger: ledger,
247307

248-
stateResponseCh: make(chan protoext.ReceivedMessage, defChannelBufferSize),
308+
stateResponseCh: make(chan protoext.ReceivedMessage, config.ChannelBufferSize),
249309

250-
stateRequestCh: make(chan protoext.ReceivedMessage, defChannelBufferSize),
310+
stateRequestCh: make(chan protoext.ReceivedMessage, config.ChannelBufferSize),
251311

252312
stopCh: make(chan struct{}, 1),
253313

@@ -260,6 +320,8 @@ func NewGossipStateProvider(chainID string, services *ServicesMediator, ledger l
260320
requestValidator: &stateRequestValidator{},
261321

262322
blockingMode: blockingMode,
323+
324+
config: config,
263325
}
264326

265327
logger.Infof("Updating metadata information, "+
@@ -273,8 +335,10 @@ func NewGossipStateProvider(chainID string, services *ServicesMediator, ledger l
273335
go s.listen()
274336
// Deliver in order messages into the incoming channel
275337
go s.deliverPayloads()
276-
// Execute anti entropy to fill missing gaps
277-
go s.antiEntropy()
338+
if s.config.EnableStateTransfer {
339+
// Execute anti entropy to fill missing gaps
340+
go s.antiEntropy()
341+
}
278342
// Taking care of state request messages
279343
go s.processStateRequests()
280344

@@ -381,7 +445,7 @@ func (s *GossipStateProviderImpl) directMessage(msg protoext.ReceivedMessage) {
381445
incoming := msg.GetGossipMessage()
382446

383447
if incoming.GetStateRequest() != nil {
384-
if len(s.stateRequestCh) < defChannelBufferSize {
448+
if len(s.stateRequestCh) < s.config.ChannelBufferSize {
385449
// Forward state request to the channel, if there are too
386450
// many message of state request ignore to avoid flooding.
387451
s.stateRequestCh <- msg
@@ -418,7 +482,7 @@ func (s *GossipStateProviderImpl) handleStateRequest(msg protoext.ReceivedMessag
418482
}
419483
request := msg.GetGossipMessage().GetStateRequest()
420484

421-
if err := s.requestValidator.validate(request); err != nil {
485+
if err := s.requestValidator.validate(request, s.config.AntiEntropyBatchSize); err != nil {
422486
logger.Errorf("State request validation failed, %s. Ignoring request...", err)
423487
return
424488
}
@@ -609,7 +673,7 @@ func (s *GossipStateProviderImpl) antiEntropy() {
609673
case <-s.stopCh:
610674
s.stopCh <- struct{}{}
611675
return
612-
case <-time.After(defAntiEntropyInterval):
676+
case <-time.After(s.config.AntiEntropyInterval):
613677
ourHeight, err := s.ledger.LedgerHeight()
614678
if err != nil {
615679
// Unable to read from ledger continue to the next round
@@ -654,15 +718,15 @@ func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64)
654718
defer atomic.StoreInt32(&s.stateTransferActive, 0)
655719

656720
for prev := start; prev <= end; {
657-
next := min(end, prev+defAntiEntropyBatchSize)
721+
next := min(end, prev+s.config.AntiEntropyBatchSize)
658722

659723
gossipMsg := s.stateRequestMessage(prev, next)
660724

661725
responseReceived := false
662726
tryCounts := 0
663727

664728
for !responseReceived {
665-
if tryCounts > defAntiEntropyMaxRetries {
729+
if tryCounts > s.config.AntiEntropyMaxRetries {
666730
logger.Warningf("Wasn't able to get blocks in range [%d...%d), after %d retries",
667731
prev, next, tryCounts)
668732
return
@@ -696,7 +760,7 @@ func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64)
696760
}
697761
prev = index + 1
698762
responseReceived = true
699-
case <-time.After(defAntiEntropyStateResponseTimeout):
763+
case <-time.After(s.config.AntiEntropyStateResponseTimeout):
700764
case <-s.stopCh:
701765
s.stopCh <- struct{}{}
702766
return
@@ -778,15 +842,16 @@ func (s *GossipStateProviderImpl) addPayload(payload *proto.Payload, blockingMod
778842
return errors.Wrap(err, "Failed obtaining ledger height")
779843
}
780844

781-
if !blockingMode && payload.SeqNum-height >= defMaxBlockDistance {
845+
if !blockingMode && payload.SeqNum-height >= uint64(s.config.MaxBlockDistance) {
782846
return errors.Errorf("Ledger height is at %d, cannot enqueue block with sequence of %d", height, payload.SeqNum)
783847
}
784848

785-
for blockingMode && s.payloads.Size() > defMaxBlockDistance*2 {
849+
for blockingMode && s.payloads.Size() > s.config.MaxBlockDistance*2 {
786850
time.Sleep(enqueueRetryInterval)
787851
}
788852

789853
s.payloads.Push(payload)
854+
logger.Debugf("Blocks payloads buffer size for channel [%s] is %d blocks", s.chainID, s.payloads.Size())
790855
return nil
791856
}
792857

gossip/state/state_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,21 +1692,21 @@ func TestStateRequestValidator(t *testing.T) {
16921692
err := validator.validate(&proto.RemoteStateRequest{
16931693
StartSeqNum: 10,
16941694
EndSeqNum: 5,
1695-
})
1695+
}, defAntiEntropyBatchSize)
16961696
assert.Contains(t, err.Error(), "Invalid sequence interval [10...5).")
16971697
assert.Error(t, err)
16981698

16991699
err = validator.validate(&proto.RemoteStateRequest{
17001700
StartSeqNum: 10,
17011701
EndSeqNum: 30,
1702-
})
1702+
}, defAntiEntropyBatchSize)
17031703
assert.Contains(t, err.Error(), "Requesting blocks range [10-30) greater than configured")
17041704
assert.Error(t, err)
17051705

17061706
err = validator.validate(&proto.RemoteStateRequest{
17071707
StartSeqNum: 10,
17081708
EndSeqNum: 20,
1709-
})
1709+
}, defAntiEntropyBatchSize)
17101710
assert.NoError(t, err)
17111711
}
17121712

sampleconfig/core.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,30 @@ peer:
203203
# reconciliationEnabled is a flag that indicates whether private data reconciliation is enable or not.
204204
reconciliationEnabled: true
205205

206+
# Gossip state transfer related configuration
207+
state:
208+
# indicates whenever state transfer is enabled or not
209+
# default value is true, i.e. state transfer is active
210+
# and takes care to sync up missing blocks allowing
211+
# lagging peer to catch up to speed with rest network
212+
enabled: true
213+
# checkInterval interval to check whether peer is lagging behind enough to
214+
# request blocks via state transfer from another peer.
215+
checkInterval: 10s
216+
# responseTimeout amount of time to wait for state transfer response from
217+
# other peers
218+
responseTimeout: 3s
219+
# batchSize the number of blocks to request via state transfer from another peer
220+
batchSize: 10
221+
# blockBufferSize reflects the size of the re-ordering buffer
222+
# which captures blocks and takes care to deliver them in order
223+
# down to the ledger layer. The actually buffer size is bounded between
224+
# 0 and 2*blockBufferSize, each channel maintains its own buffer
225+
blockBufferSize: 100
226+
# maxRetries maximum number of re-tries to ask
227+
# for single state transfer request
228+
maxRetries: 3
229+
206230
# TLS Settings
207231
# Note that peer-chaincode connections through chaincodeListenAddress is
208232
# not mutual TLS auth. See comments on chaincodeListenAddress for more info

0 commit comments

Comments
 (0)