github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/enqueue.go (about)

     1  package engine
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/rs/zerolog"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/utils/logging"
    10  )
    11  
    12  type Message struct {
    13  	OriginID flow.Identifier
    14  	Payload  interface{}
    15  }
    16  
    17  // MessageStore is the interface to abstract how messages are buffered in memory
    18  // while waiting to be processed.
    19  type MessageStore interface {
    20  	// Put adds the message to the message store. It returns true if the message
    21  	// is stored, and false if it is immediately dropped.
    22  	// Note: depending on the implementation, message stores might drop messages
    23  	// later according to their internal ejection policy. In other words, a return
    24  	// value of `true` does _not imply_ that the message is eventually processed.
    25  	Put(*Message) bool
    26  	// Get retrieves the next message from the message store. It returns true if
    27  	// a message is retrieved, and false if the message store is empty.
    28  	Get() (*Message, bool)
    29  }
    30  
    31  type Pattern struct {
    32  	// Match is a function to match a message to this pattern, typically by payload type.
    33  	Match MatchFunc
    34  	// Map is a function to apply to messages before storing them. If not provided, then the message is stored in its original form.
    35  	Map MapFunc
    36  	// Store is an abstract message store where we will store the message upon receipt.
    37  	Store MessageStore
    38  }
    39  
    40  type FilterFunc func(*Message) bool
    41  
    42  type MatchFunc func(*Message) bool
    43  
    44  type MapFunc func(*Message) (*Message, bool)
    45  
    46  func MatchType[T any](m *Message) bool {
    47  	_, ok := m.Payload.(T)
    48  	return ok
    49  }
    50  
    51  type MessageHandler struct {
    52  	log      zerolog.Logger
    53  	notifier Notifier
    54  	patterns []Pattern
    55  }
    56  
    57  func NewMessageHandler(log zerolog.Logger, notifier Notifier, patterns ...Pattern) *MessageHandler {
    58  	return &MessageHandler{
    59  		log:      log.With().Str("component", "message_handler").Logger(),
    60  		notifier: notifier,
    61  		patterns: patterns,
    62  	}
    63  }
    64  
    65  // Process iterates over the internal processing patterns and determines if the payload matches.
    66  // The _first_ matching pattern processes the payload.
    67  // Returns
    68  //   - IncompatibleInputTypeError if no matching processor was found
    69  //   - All other errors are potential symptoms of internal state corruption or bugs (fatal).
    70  func (e *MessageHandler) Process(originID flow.Identifier, payload interface{}) error {
    71  	msg := &Message{
    72  		OriginID: originID,
    73  		Payload:  payload,
    74  	}
    75  
    76  	for _, pattern := range e.patterns {
    77  		if pattern.Match(msg) {
    78  			var keep bool
    79  			if pattern.Map != nil {
    80  				msg, keep = pattern.Map(msg)
    81  				if !keep {
    82  					return nil
    83  				}
    84  			}
    85  
    86  			ok := pattern.Store.Put(msg)
    87  			if !ok {
    88  				e.log.Warn().
    89  					Str("msg_type", logging.Type(payload)).
    90  					Hex("origin_id", originID[:]).
    91  					Msg("failed to store message - discarding")
    92  				return nil
    93  			}
    94  			e.notifier.Notify()
    95  
    96  			// message can only be matched by one pattern, and processed by one handler
    97  			return nil
    98  		}
    99  	}
   100  
   101  	return fmt.Errorf("no matching processor for message of type %T from origin %x: %w", payload, originID[:], IncompatibleInputTypeError)
   102  }
   103  
   104  func (e *MessageHandler) GetNotifier() <-chan struct{} {
   105  	return e.notifier.Channel()
   106  }