github.com/koko1123/flow-go-1@v0.29.6/engine/consensus/ingestion/engine.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package ingestion 4 5 import ( 6 "context" 7 "fmt" 8 9 "github.com/rs/zerolog" 10 11 "github.com/koko1123/flow-go-1/engine" 12 "github.com/koko1123/flow-go-1/engine/common/fifoqueue" 13 "github.com/koko1123/flow-go-1/model/flow" 14 "github.com/koko1123/flow-go-1/module" 15 "github.com/koko1123/flow-go-1/module/component" 16 "github.com/koko1123/flow-go-1/module/irrecoverable" 17 "github.com/koko1123/flow-go-1/module/metrics" 18 "github.com/koko1123/flow-go-1/network" 19 "github.com/koko1123/flow-go-1/network/channels" 20 ) 21 22 // defaultGuaranteeQueueCapacity maximum capacity of pending events queue, everything above will be dropped 23 const defaultGuaranteeQueueCapacity = 1000 24 25 // defaultIngestionEngineWorkers number of goroutines engine will use for processing events 26 const defaultIngestionEngineWorkers = 3 27 28 // Engine represents the ingestion engine, used to funnel collections from a 29 // cluster of collection nodes to the set of consensus nodes. It represents the 30 // link between collection nodes and consensus nodes and has a counterpart with 31 // the same engine ID in the collection node. 32 type Engine struct { 33 component.Component 34 log zerolog.Logger // used to log relevant actions with context 35 me module.Local // used to access local node information 36 con network.Conduit // conduit to receive/send guarantees 37 core *Core // core logic of processing guarantees 38 pendingGuarantees engine.MessageStore // message store of pending events 39 messageHandler *engine.MessageHandler // message handler for incoming events 40 } 41 42 // New creates a new collection propagation engine. 43 func New( 44 log zerolog.Logger, 45 engineMetrics module.EngineMetrics, 46 net network.Network, 47 me module.Local, 48 core *Core, 49 ) (*Engine, error) { 50 51 logger := log.With().Str("ingestion", "engine").Logger() 52 53 guaranteesQueue, err := fifoqueue.NewFifoQueue( 54 defaultGuaranteeQueueCapacity, 55 fifoqueue.WithLengthObserver(func(len int) { core.mempool.MempoolEntries(metrics.ResourceCollectionGuaranteesQueue, uint(len)) }), 56 ) 57 if err != nil { 58 return nil, fmt.Errorf("could not create guarantees queue: %w", err) 59 } 60 61 pendingGuarantees := &engine.FifoMessageStore{ 62 FifoQueue: guaranteesQueue, 63 } 64 65 handler := engine.NewMessageHandler( 66 logger, 67 engine.NewNotifier(), 68 engine.Pattern{ 69 Match: func(msg *engine.Message) bool { 70 _, ok := msg.Payload.(*flow.CollectionGuarantee) 71 if ok { 72 engineMetrics.MessageReceived(metrics.EngineConsensusIngestion, metrics.MessageCollectionGuarantee) 73 } 74 return ok 75 }, 76 Store: pendingGuarantees, 77 }, 78 ) 79 80 // initialize the propagation engine with its dependencies 81 e := &Engine{ 82 log: logger, 83 me: me, 84 core: core, 85 pendingGuarantees: pendingGuarantees, 86 messageHandler: handler, 87 } 88 89 componentManagerBuilder := component.NewComponentManagerBuilder() 90 91 for i := 0; i < defaultIngestionEngineWorkers; i++ { 92 componentManagerBuilder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 93 ready() 94 err := e.loop(ctx) 95 if err != nil { 96 ctx.Throw(err) 97 } 98 }) 99 } 100 101 e.Component = componentManagerBuilder.Build() 102 103 // register the engine with the network layer and store the conduit 104 con, err := net.Register(channels.ReceiveGuarantees, e) 105 if err != nil { 106 return nil, fmt.Errorf("could not register engine: %w", err) 107 } 108 e.con = con 109 return e, nil 110 } 111 112 // SubmitLocal submits an event originating on the local node. 113 func (e *Engine) SubmitLocal(event interface{}) { 114 err := e.ProcessLocal(event) 115 if err != nil { 116 e.log.Fatal().Err(err).Msg("internal error processing event") 117 } 118 } 119 120 // Submit submits the given event from the node with the given origin ID 121 // for processing in a non-blocking manner. It returns instantly and logs 122 // a potential processing error internally when done. 123 func (e *Engine) Submit(channel channels.Channel, originID flow.Identifier, event interface{}) { 124 err := e.Process(channel, originID, event) 125 if err != nil { 126 e.log.Fatal().Err(err).Msg("internal error processing event") 127 } 128 } 129 130 // ProcessLocal processes an event originating on the local node. 131 func (e *Engine) ProcessLocal(event interface{}) error { 132 return e.messageHandler.Process(e.me.NodeID(), event) 133 } 134 135 // Process processes the given event from the node with the given origin ID in 136 // a blocking manner. It returns error only in unexpected scenario. 137 func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, event interface{}) error { 138 err := e.messageHandler.Process(originID, event) 139 if err != nil { 140 if engine.IsIncompatibleInputTypeError(err) { 141 e.log.Warn().Msgf("%v delivered unsupported message %T through %v", originID, event, channel) 142 return nil 143 } 144 return fmt.Errorf("unexpected error while processing engine message: %w", err) 145 } 146 return nil 147 } 148 149 // processAvailableMessages processes the given ingestion engine event. 150 func (e *Engine) processAvailableMessages(ctx context.Context) error { 151 for { 152 select { 153 case <-ctx.Done(): 154 return nil 155 default: // fall through to business logic 156 } 157 158 msg, ok := e.pendingGuarantees.Get() 159 if ok { 160 originID := msg.OriginID 161 err := e.core.OnGuarantee(originID, msg.Payload.(*flow.CollectionGuarantee)) 162 if err != nil { 163 if engine.IsInvalidInputError(err) { 164 e.log.Error().Str("origin", originID.String()).Err(err).Msg("received invalid collection guarantee") 165 return nil 166 } 167 if engine.IsOutdatedInputError(err) { 168 e.log.Warn().Str("origin", originID.String()).Err(err).Msg("received outdated collection guarantee") 169 return nil 170 } 171 if engine.IsUnverifiableInputError(err) { 172 e.log.Warn().Str("origin", originID.String()).Err(err).Msg("received unverifiable collection guarantee") 173 return nil 174 } 175 return fmt.Errorf("processing collection guarantee unexpected err: %w", err) 176 } 177 178 continue 179 } 180 181 // when there is no more messages in the queue, back to the loop to wait 182 // for the next incoming message to arrive. 183 return nil 184 } 185 } 186 187 func (e *Engine) loop(ctx context.Context) error { 188 for { 189 select { 190 case <-ctx.Done(): 191 return nil 192 case <-e.messageHandler.GetNotifier(): 193 err := e.processAvailableMessages(ctx) 194 if err != nil { 195 return fmt.Errorf("internal error processing queued message: %w", err) 196 } 197 } 198 } 199 }