github.com/koko1123/flow-go-1@v0.29.6/engine/verification/assigner/blockconsumer/consumer.go (about)

     1  package blockconsumer
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/rs/zerolog"
     7  
     8  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
     9  	"github.com/koko1123/flow-go-1/engine"
    10  	"github.com/koko1123/flow-go-1/engine/verification/assigner"
    11  	"github.com/koko1123/flow-go-1/module"
    12  	"github.com/koko1123/flow-go-1/module/jobqueue"
    13  	"github.com/koko1123/flow-go-1/state/protocol"
    14  	"github.com/koko1123/flow-go-1/storage"
    15  )
    16  
    17  // DefaultBlockWorkers is the number of blocks processed in parallel.
    18  const DefaultBlockWorkers = uint64(2)
    19  
    20  // BlockConsumer listens to the OnFinalizedBlock event
    21  // and notifies the consumer to check in the job queue
    22  // (i.e., its block reader) for new block jobs.
    23  type BlockConsumer struct {
    24  	consumer     module.JobConsumer
    25  	defaultIndex uint64
    26  	unit         *engine.Unit
    27  	metrics      module.VerificationMetrics
    28  }
    29  
    30  // defaultProcessedIndex returns the last sealed block height from the protocol state.
    31  //
    32  // The BlockConsumer utilizes this return height to fetch and consume block jobs from
    33  // jobs queue the first time it initializes.
    34  func defaultProcessedIndex(state protocol.State) (uint64, error) {
    35  	final, err := state.Sealed().Head()
    36  	if err != nil {
    37  		return 0, fmt.Errorf("could not get finalized height: %w", err)
    38  	}
    39  	return final.Height, nil
    40  }
    41  
    42  // NewBlockConsumer creates a new consumer and returns the default processed
    43  // index for initializing the processed index in storage.
    44  func NewBlockConsumer(log zerolog.Logger,
    45  	metrics module.VerificationMetrics,
    46  	processedHeight storage.ConsumerProgress,
    47  	blocks storage.Blocks,
    48  	state protocol.State,
    49  	blockProcessor assigner.FinalizedBlockProcessor,
    50  	maxProcessing uint64) (*BlockConsumer, uint64, error) {
    51  
    52  	lg := log.With().Str("module", "block_consumer").Logger()
    53  
    54  	// wires blockProcessor as the worker. The block consumer will
    55  	// invoke instances of worker concurrently to process block jobs.
    56  	worker := newWorker(blockProcessor)
    57  	blockProcessor.WithBlockConsumerNotifier(worker)
    58  
    59  	// the block reader is where the consumer reads new finalized blocks from (i.e., jobs).
    60  	jobs := jobqueue.NewFinalizedBlockReader(state, blocks)
    61  
    62  	consumer := jobqueue.NewConsumer(lg, jobs, processedHeight, worker, maxProcessing, 0)
    63  	defaultIndex, err := defaultProcessedIndex(state)
    64  	if err != nil {
    65  		return nil, 0, fmt.Errorf("could not read default processed index: %w", err)
    66  	}
    67  
    68  	blockConsumer := &BlockConsumer{
    69  		consumer:     consumer,
    70  		defaultIndex: defaultIndex,
    71  		unit:         engine.NewUnit(),
    72  		metrics:      metrics,
    73  	}
    74  	worker.withBlockConsumer(blockConsumer)
    75  
    76  	return blockConsumer, defaultIndex, nil
    77  }
    78  
    79  // NotifyJobIsDone is invoked by the worker to let the consumer know that it is done
    80  // processing a (block) job.
    81  func (c *BlockConsumer) NotifyJobIsDone(jobID module.JobID) {
    82  	processedIndex := c.consumer.NotifyJobIsDone(jobID)
    83  	c.metrics.OnBlockConsumerJobDone(processedIndex)
    84  }
    85  
    86  // Size returns number of in-memory block jobs that block consumer is processing.
    87  func (c *BlockConsumer) Size() uint {
    88  	return c.consumer.Size()
    89  }
    90  
    91  // OnFinalizedBlock implements FinalizationConsumer, and is invoked by the follower engine whenever
    92  // a new block is finalized.
    93  // In this implementation for block consumer, invoking OnFinalizedBlock is enough to only notify the consumer
    94  // to check its internal queue and move its processing index ahead to the next height if there are workers available.
    95  // The consumer retrieves the new blocks from its block reader module, hence it does not need to use the parameter
    96  // of OnFinalizedBlock here.
    97  func (c *BlockConsumer) OnFinalizedBlock(*model.Block) {
    98  	c.unit.Launch(c.consumer.Check)
    99  }
   100  
   101  // OnBlockIncorporated is to implement FinalizationConsumer
   102  func (c *BlockConsumer) OnBlockIncorporated(*model.Block) {}
   103  
   104  // OnDoubleProposeDetected is to implement FinalizationConsumer
   105  func (c *BlockConsumer) OnDoubleProposeDetected(*model.Block, *model.Block) {}
   106  
   107  func (c *BlockConsumer) Ready() <-chan struct{} {
   108  	err := c.consumer.Start(c.defaultIndex)
   109  	if err != nil {
   110  		panic(fmt.Errorf("could not start block consumer for finder engine: %w", err))
   111  	}
   112  
   113  	ready := make(chan struct{})
   114  	close(ready)
   115  	return ready
   116  }
   117  
   118  func (c *BlockConsumer) Done() <-chan struct{} {
   119  	ready := make(chan struct{})
   120  	go func() {
   121  		completeChan := c.unit.Done()
   122  		c.consumer.Stop()
   123  		<-completeChan
   124  		close(ready)
   125  	}()
   126  	return ready
   127  }