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  }