github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/events.go (about)

     1  package badger
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/dgraph-io/badger/v2"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/module"
    10  	"github.com/onflow/flow-go/module/metrics"
    11  	"github.com/onflow/flow-go/storage"
    12  	"github.com/onflow/flow-go/storage/badger/operation"
    13  )
    14  
    15  type Events struct {
    16  	db    *badger.DB
    17  	cache *Cache[flow.Identifier, []flow.Event]
    18  }
    19  
    20  func NewEvents(collector module.CacheMetrics, db *badger.DB) *Events {
    21  	retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) ([]flow.Event, error) {
    22  		var events []flow.Event
    23  		return func(tx *badger.Txn) ([]flow.Event, error) {
    24  			err := operation.LookupEventsByBlockID(blockID, &events)(tx)
    25  			return events, handleError(err, flow.Event{})
    26  		}
    27  	}
    28  
    29  	return &Events{
    30  		db: db,
    31  		cache: newCache[flow.Identifier, []flow.Event](collector, metrics.ResourceEvents,
    32  			withStore(noopStore[flow.Identifier, []flow.Event]),
    33  			withRetrieve(retrieve)),
    34  	}
    35  }
    36  
    37  // BatchStore stores events keyed by a blockID in provided batch
    38  // No errors are expected during normal operation, but it may return generic error
    39  // if badger fails to process request
    40  func (e *Events) BatchStore(blockID flow.Identifier, blockEvents []flow.EventsList, batch storage.BatchStorage) error {
    41  	writeBatch := batch.GetWriter()
    42  
    43  	// pre-allocating and indexing slice is faster than appending
    44  	sliceSize := 0
    45  	for _, b := range blockEvents {
    46  		sliceSize += len(b)
    47  	}
    48  
    49  	combinedEvents := make([]flow.Event, sliceSize)
    50  
    51  	eventIndex := 0
    52  
    53  	for _, events := range blockEvents {
    54  		for _, event := range events {
    55  			err := operation.BatchInsertEvent(blockID, event)(writeBatch)
    56  			if err != nil {
    57  				return fmt.Errorf("cannot batch insert event: %w", err)
    58  			}
    59  			combinedEvents[eventIndex] = event
    60  			eventIndex++
    61  		}
    62  	}
    63  
    64  	callback := func() {
    65  		e.cache.Insert(blockID, combinedEvents)
    66  	}
    67  	batch.OnSucceed(callback)
    68  	return nil
    69  }
    70  
    71  // Store will store events for the given block ID
    72  func (e *Events) Store(blockID flow.Identifier, blockEvents []flow.EventsList) error {
    73  	batch := NewBatch(e.db)
    74  
    75  	err := e.BatchStore(blockID, blockEvents, batch)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	err = batch.Flush()
    81  	if err != nil {
    82  		return fmt.Errorf("cannot flush batch: %w", err)
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  // ByBlockID returns the events for the given block ID
    89  // Note: This method will return an empty slice and no error if no entries for the blockID are found
    90  func (e *Events) ByBlockID(blockID flow.Identifier) ([]flow.Event, error) {
    91  	tx := e.db.NewTransaction(false)
    92  	defer tx.Discard()
    93  	val, err := e.cache.Get(blockID)(tx)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return val, nil
    98  }
    99  
   100  // ByBlockIDTransactionID returns the events for the given block ID and transaction ID
   101  // Note: This method will return an empty slice and no error if no entries for the blockID are found
   102  func (e *Events) ByBlockIDTransactionID(blockID flow.Identifier, txID flow.Identifier) ([]flow.Event, error) {
   103  	events, err := e.ByBlockID(blockID)
   104  	if err != nil {
   105  		return nil, handleError(err, flow.Event{})
   106  	}
   107  
   108  	var matched []flow.Event
   109  	for _, event := range events {
   110  		if event.TransactionID == txID {
   111  			matched = append(matched, event)
   112  		}
   113  	}
   114  	return matched, nil
   115  }
   116  
   117  // ByBlockIDTransactionIndex returns the events for the given block ID and transaction index
   118  // Note: This method will return an empty slice and no error if no entries for the blockID are found
   119  func (e *Events) ByBlockIDTransactionIndex(blockID flow.Identifier, txIndex uint32) ([]flow.Event, error) {
   120  	events, err := e.ByBlockID(blockID)
   121  	if err != nil {
   122  		return nil, handleError(err, flow.Event{})
   123  	}
   124  
   125  	var matched []flow.Event
   126  	for _, event := range events {
   127  		if event.TransactionIndex == txIndex {
   128  			matched = append(matched, event)
   129  		}
   130  	}
   131  	return matched, nil
   132  }
   133  
   134  // ByBlockIDEventType returns the events for the given block ID and event type
   135  // Note: This method will return an empty slice and no error if no entries for the blockID are found
   136  func (e *Events) ByBlockIDEventType(blockID flow.Identifier, eventType flow.EventType) ([]flow.Event, error) {
   137  	events, err := e.ByBlockID(blockID)
   138  	if err != nil {
   139  		return nil, handleError(err, flow.Event{})
   140  	}
   141  
   142  	var matched []flow.Event
   143  	for _, event := range events {
   144  		if event.Type == eventType {
   145  			matched = append(matched, event)
   146  		}
   147  	}
   148  	return matched, nil
   149  }
   150  
   151  // RemoveByBlockID removes events by block ID
   152  func (e *Events) RemoveByBlockID(blockID flow.Identifier) error {
   153  	return e.db.Update(operation.RemoveEventsByBlockID(blockID))
   154  }
   155  
   156  // BatchRemoveByBlockID removes events keyed by a blockID in provided batch
   157  // No errors are expected during normal operation, even if no entries are matched.
   158  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   159  func (e *Events) BatchRemoveByBlockID(blockID flow.Identifier, batch storage.BatchStorage) error {
   160  	writeBatch := batch.GetWriter()
   161  	return e.db.View(operation.BatchRemoveEventsByBlockID(blockID, writeBatch))
   162  }
   163  
   164  type ServiceEvents struct {
   165  	db    *badger.DB
   166  	cache *Cache[flow.Identifier, []flow.Event]
   167  }
   168  
   169  func NewServiceEvents(collector module.CacheMetrics, db *badger.DB) *ServiceEvents {
   170  	retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) ([]flow.Event, error) {
   171  		var events []flow.Event
   172  		return func(tx *badger.Txn) ([]flow.Event, error) {
   173  			err := operation.LookupServiceEventsByBlockID(blockID, &events)(tx)
   174  			return events, handleError(err, flow.Event{})
   175  		}
   176  	}
   177  
   178  	return &ServiceEvents{
   179  		db: db,
   180  		cache: newCache[flow.Identifier, []flow.Event](collector, metrics.ResourceEvents,
   181  			withStore(noopStore[flow.Identifier, []flow.Event]),
   182  			withRetrieve(retrieve)),
   183  	}
   184  }
   185  
   186  // BatchStore stores service events keyed by a blockID in provided batch
   187  // No errors are expected during normal operation, even if no entries are matched.
   188  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   189  func (e *ServiceEvents) BatchStore(blockID flow.Identifier, events []flow.Event, batch storage.BatchStorage) error {
   190  	writeBatch := batch.GetWriter()
   191  	for _, event := range events {
   192  		err := operation.BatchInsertServiceEvent(blockID, event)(writeBatch)
   193  		if err != nil {
   194  			return fmt.Errorf("cannot batch insert service event: %w", err)
   195  		}
   196  	}
   197  
   198  	callback := func() {
   199  		e.cache.Insert(blockID, events)
   200  	}
   201  	batch.OnSucceed(callback)
   202  	return nil
   203  }
   204  
   205  // ByBlockID returns the events for the given block ID
   206  func (e *ServiceEvents) ByBlockID(blockID flow.Identifier) ([]flow.Event, error) {
   207  	tx := e.db.NewTransaction(false)
   208  	defer tx.Discard()
   209  	val, err := e.cache.Get(blockID)(tx)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	return val, nil
   214  }
   215  
   216  // RemoveByBlockID removes service events by block ID
   217  func (e *ServiceEvents) RemoveByBlockID(blockID flow.Identifier) error {
   218  	return e.db.Update(operation.RemoveServiceEventsByBlockID(blockID))
   219  }
   220  
   221  // BatchRemoveByBlockID removes service events keyed by a blockID in provided batch
   222  // No errors are expected during normal operation, even if no entries are matched.
   223  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   224  func (e *ServiceEvents) BatchRemoveByBlockID(blockID flow.Identifier, batch storage.BatchStorage) error {
   225  	writeBatch := batch.GetWriter()
   226  	return e.db.View(operation.BatchRemoveServiceEventsByBlockID(blockID, writeBatch))
   227  }