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 }