github.com/anjalikarhana/fabric@v2.1.1+incompatible/orderer/common/blockcutter/blockcutter.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package blockcutter 8 9 import ( 10 "time" 11 12 cb "github.com/hyperledger/fabric-protos-go/common" 13 "github.com/hyperledger/fabric/common/channelconfig" 14 "github.com/hyperledger/fabric/common/flogging" 15 ) 16 17 var logger = flogging.MustGetLogger("orderer.common.blockcutter") 18 19 type OrdererConfigFetcher interface { 20 OrdererConfig() (channelconfig.Orderer, bool) 21 } 22 23 // Receiver defines a sink for the ordered broadcast messages 24 type Receiver interface { 25 // Ordered should be invoked sequentially as messages are ordered 26 // Each batch in `messageBatches` will be wrapped into a block. 27 // `pending` indicates if there are still messages pending in the receiver. 28 Ordered(msg *cb.Envelope) (messageBatches [][]*cb.Envelope, pending bool) 29 30 // Cut returns the current batch and starts a new one 31 Cut() []*cb.Envelope 32 } 33 34 type receiver struct { 35 sharedConfigFetcher OrdererConfigFetcher 36 pendingBatch []*cb.Envelope 37 pendingBatchSizeBytes uint32 38 39 PendingBatchStartTime time.Time 40 ChannelID string 41 Metrics *Metrics 42 } 43 44 // NewReceiverImpl creates a Receiver implementation based on the given configtxorderer manager 45 func NewReceiverImpl(channelID string, sharedConfigFetcher OrdererConfigFetcher, metrics *Metrics) Receiver { 46 return &receiver{ 47 sharedConfigFetcher: sharedConfigFetcher, 48 Metrics: metrics, 49 ChannelID: channelID, 50 } 51 } 52 53 // Ordered should be invoked sequentially as messages are ordered 54 // 55 // messageBatches length: 0, pending: false 56 // - impossible, as we have just received a message 57 // messageBatches length: 0, pending: true 58 // - no batch is cut and there are messages pending 59 // messageBatches length: 1, pending: false 60 // - the message count reaches BatchSize.MaxMessageCount 61 // messageBatches length: 1, pending: true 62 // - the current message will cause the pending batch size in bytes to exceed BatchSize.PreferredMaxBytes. 63 // messageBatches length: 2, pending: false 64 // - the current message size in bytes exceeds BatchSize.PreferredMaxBytes, therefore isolated in its own batch. 65 // messageBatches length: 2, pending: true 66 // - impossible 67 // 68 // Note that messageBatches can not be greater than 2. 69 func (r *receiver) Ordered(msg *cb.Envelope) (messageBatches [][]*cb.Envelope, pending bool) { 70 if len(r.pendingBatch) == 0 { 71 // We are beginning a new batch, mark the time 72 r.PendingBatchStartTime = time.Now() 73 } 74 75 ordererConfig, ok := r.sharedConfigFetcher.OrdererConfig() 76 if !ok { 77 logger.Panicf("Could not retrieve orderer config to query batch parameters, block cutting is not possible") 78 } 79 80 batchSize := ordererConfig.BatchSize() 81 82 messageSizeBytes := messageSizeBytes(msg) 83 if messageSizeBytes > batchSize.PreferredMaxBytes { 84 logger.Debugf("The current message, with %v bytes, is larger than the preferred batch size of %v bytes and will be isolated.", messageSizeBytes, batchSize.PreferredMaxBytes) 85 86 // cut pending batch, if it has any messages 87 if len(r.pendingBatch) > 0 { 88 messageBatch := r.Cut() 89 messageBatches = append(messageBatches, messageBatch) 90 } 91 92 // create new batch with single message 93 messageBatches = append(messageBatches, []*cb.Envelope{msg}) 94 95 // Record that this batch took no time to fill 96 r.Metrics.BlockFillDuration.With("channel", r.ChannelID).Observe(0) 97 98 return 99 } 100 101 messageWillOverflowBatchSizeBytes := r.pendingBatchSizeBytes+messageSizeBytes > batchSize.PreferredMaxBytes 102 103 if messageWillOverflowBatchSizeBytes { 104 logger.Debugf("The current message, with %v bytes, will overflow the pending batch of %v bytes.", messageSizeBytes, r.pendingBatchSizeBytes) 105 logger.Debugf("Pending batch would overflow if current message is added, cutting batch now.") 106 messageBatch := r.Cut() 107 r.PendingBatchStartTime = time.Now() 108 messageBatches = append(messageBatches, messageBatch) 109 } 110 111 logger.Debugf("Enqueuing message into batch") 112 r.pendingBatch = append(r.pendingBatch, msg) 113 r.pendingBatchSizeBytes += messageSizeBytes 114 pending = true 115 116 if uint32(len(r.pendingBatch)) >= batchSize.MaxMessageCount { 117 logger.Debugf("Batch size met, cutting batch") 118 messageBatch := r.Cut() 119 messageBatches = append(messageBatches, messageBatch) 120 pending = false 121 } 122 123 return 124 } 125 126 // Cut returns the current batch and starts a new one 127 func (r *receiver) Cut() []*cb.Envelope { 128 if r.pendingBatch != nil { 129 r.Metrics.BlockFillDuration.With("channel", r.ChannelID).Observe(time.Since(r.PendingBatchStartTime).Seconds()) 130 } 131 r.PendingBatchStartTime = time.Time{} 132 batch := r.pendingBatch 133 r.pendingBatch = nil 134 r.pendingBatchSizeBytes = 0 135 return batch 136 } 137 138 func messageSizeBytes(message *cb.Envelope) uint32 { 139 return uint32(len(message.Payload) + len(message.Signature)) 140 }