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 }