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 }