github.com/onflow/flow-go@v0.33.17/engine/access/state_stream/backend/backend_events.go (about) 1 package backend 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/rs/zerolog" 9 10 "github.com/onflow/flow-go/engine" 11 rpcbackend "github.com/onflow/flow-go/engine/access/rpc/backend" 12 "github.com/onflow/flow-go/engine/access/state_stream" 13 "github.com/onflow/flow-go/model/flow" 14 "github.com/onflow/flow-go/storage" 15 "github.com/onflow/flow-go/utils/logging" 16 ) 17 18 type EventsResponse struct { 19 BlockID flow.Identifier 20 Height uint64 21 Events flow.EventsList 22 } 23 24 type EventsBackend struct { 25 log zerolog.Logger 26 headers storage.Headers 27 broadcaster *engine.Broadcaster 28 sendTimeout time.Duration 29 responseLimit float64 30 sendBufferSize int 31 32 getExecutionData GetExecutionDataFunc 33 getStartHeight GetStartHeightFunc 34 35 useIndex bool 36 eventsIndex *rpcbackend.EventsIndex 37 } 38 39 func (b *EventsBackend) SubscribeEvents(ctx context.Context, startBlockID flow.Identifier, startHeight uint64, filter state_stream.EventFilter) state_stream.Subscription { 40 nextHeight, err := b.getStartHeight(startBlockID, startHeight) 41 if err != nil { 42 return NewFailedSubscription(err, "could not get start height") 43 } 44 45 sub := NewHeightBasedSubscription(b.sendBufferSize, nextHeight, b.getResponseFactory(filter)) 46 47 go NewStreamer(b.log, b.broadcaster, b.sendTimeout, b.responseLimit, sub).Stream(ctx) 48 49 return sub 50 } 51 52 // getResponseFactory returns a function function that returns the event response for a given height. 53 func (b *EventsBackend) getResponseFactory(filter state_stream.EventFilter) GetDataByHeightFunc { 54 return func(ctx context.Context, height uint64) (response interface{}, err error) { 55 if b.useIndex { 56 response, err = b.getEventsFromStorage(height, filter) 57 } else { 58 response, err = b.getEventsFromExecutionData(ctx, height, filter) 59 } 60 61 if err == nil && b.log.GetLevel() == zerolog.TraceLevel { 62 eventsResponse := response.(*EventsResponse) 63 b.log.Trace(). 64 Hex("block_id", logging.ID(eventsResponse.BlockID)). 65 Uint64("height", height). 66 Int("events", len(eventsResponse.Events)). 67 Msg("sending events") 68 } 69 return 70 } 71 } 72 73 // getEventsFromExecutionData returns the events for a given height extractd from the execution data. 74 func (b *EventsBackend) getEventsFromExecutionData(ctx context.Context, height uint64, filter state_stream.EventFilter) (*EventsResponse, error) { 75 executionData, err := b.getExecutionData(ctx, height) 76 if err != nil { 77 return nil, fmt.Errorf("could not get execution data for block %d: %w", height, err) 78 } 79 80 var events flow.EventsList 81 for _, chunkExecutionData := range executionData.ChunkExecutionDatas { 82 events = append(events, filter.Filter(chunkExecutionData.Events)...) 83 } 84 85 return &EventsResponse{ 86 BlockID: executionData.BlockID, 87 Height: height, 88 Events: events, 89 }, nil 90 } 91 92 // getEventsFromStorage returns the events for a given height from the index storage. 93 func (b *EventsBackend) getEventsFromStorage(height uint64, filter state_stream.EventFilter) (*EventsResponse, error) { 94 blockID, err := b.headers.BlockIDByHeight(height) 95 if err != nil { 96 return nil, fmt.Errorf("could not get header for height %d: %w", height, err) 97 } 98 99 events, err := b.eventsIndex.ByBlockID(blockID, height) 100 if err != nil { 101 return nil, fmt.Errorf("could not get events for block %d: %w", height, err) 102 } 103 104 b.log.Trace(). 105 Uint64("height", height). 106 Hex("block_id", logging.ID(blockID)). 107 Int("events", len(events)). 108 Msg("events from storage") 109 110 return &EventsResponse{ 111 BlockID: blockID, 112 Height: height, 113 Events: filter.Filter(events), 114 }, nil 115 }