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  }