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  }