github.com/koko1123/flow-go-1@v0.29.6/storage/badger/my_receipts.go (about) 1 package badger 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/dgraph-io/badger/v3" 8 9 "github.com/koko1123/flow-go-1/model/flow" 10 "github.com/koko1123/flow-go-1/module" 11 "github.com/koko1123/flow-go-1/module/metrics" 12 "github.com/koko1123/flow-go-1/storage" 13 "github.com/koko1123/flow-go-1/storage/badger/operation" 14 "github.com/koko1123/flow-go-1/storage/badger/transaction" 15 ) 16 17 // MyExecutionReceipts holds and indexes Execution Receipts. 18 // MyExecutionReceipts is implemented as a wrapper around badger.ExecutionReceipts 19 // The wrapper adds the ability to "MY execution receipt", from the viewpoint 20 // of an individual Execution Node. 21 type MyExecutionReceipts struct { 22 genericReceipts *ExecutionReceipts 23 db *badger.DB 24 cache *Cache 25 } 26 27 // NewMyExecutionReceipts creates instance of MyExecutionReceipts which is a wrapper wrapper around badger.ExecutionReceipts 28 // It's useful for execution nodes to keep track of produced execution receipts. 29 func NewMyExecutionReceipts(collector module.CacheMetrics, db *badger.DB, receipts *ExecutionReceipts) *MyExecutionReceipts { 30 store := func(key interface{}, val interface{}) func(*transaction.Tx) error { 31 receipt := val.(*flow.ExecutionReceipt) 32 // assemble DB operations to store receipt (no execution) 33 storeReceiptOps := receipts.storeTx(receipt) 34 // assemble DB operations to index receipt as one of my own (no execution) 35 blockID := receipt.ExecutionResult.BlockID 36 receiptID := receipt.ID() 37 indexOwnReceiptOps := transaction.WithTx(func(tx *badger.Txn) error { 38 err := operation.IndexOwnExecutionReceipt(blockID, receiptID)(tx) 39 // check if we are storing same receipt 40 if errors.Is(err, storage.ErrAlreadyExists) { 41 var savedReceiptID flow.Identifier 42 err := operation.LookupOwnExecutionReceipt(blockID, &savedReceiptID)(tx) 43 if err != nil { 44 return err 45 } 46 47 if savedReceiptID == receiptID { 48 // if we are storing same receipt we shouldn't error 49 return nil 50 } 51 52 return fmt.Errorf("indexing my receipt %v failed: different receipt %v for the same block %v is already indexed", receiptID, 53 savedReceiptID, blockID) 54 } 55 return err 56 }) 57 58 return func(tx *transaction.Tx) error { 59 err := storeReceiptOps(tx) // execute operations to store receipt 60 if err != nil { 61 return fmt.Errorf("could not store receipt: %w", err) 62 } 63 err = indexOwnReceiptOps(tx) // execute operations to index receipt as one of my own 64 if err != nil { 65 return fmt.Errorf("could not index receipt as one of my own: %w", err) 66 } 67 return nil 68 } 69 } 70 71 retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { 72 blockID := key.(flow.Identifier) 73 74 return func(tx *badger.Txn) (interface{}, error) { 75 var receiptID flow.Identifier 76 err := operation.LookupOwnExecutionReceipt(blockID, &receiptID)(tx) 77 if err != nil { 78 return nil, fmt.Errorf("could not lookup receipt ID: %w", err) 79 } 80 receipt, err := receipts.byID(receiptID)(tx) 81 if err != nil { 82 return nil, err 83 } 84 return receipt, nil 85 } 86 } 87 88 return &MyExecutionReceipts{ 89 genericReceipts: receipts, 90 db: db, 91 cache: newCache(collector, metrics.ResourceMyReceipt, 92 withLimit(flow.DefaultTransactionExpiry+100), 93 withStore(store), 94 withRetrieve(retrieve)), 95 } 96 } 97 98 // storeMyReceipt assembles the operations to store the receipt and marks it as mine (trusted). 99 func (m *MyExecutionReceipts) storeMyReceipt(receipt *flow.ExecutionReceipt) func(*transaction.Tx) error { 100 return m.cache.PutTx(receipt.ExecutionResult.BlockID, receipt) 101 } 102 103 // storeMyReceipt assembles the operations to retrieve my receipt for the given block ID. 104 func (m *MyExecutionReceipts) myReceipt(blockID flow.Identifier) func(*badger.Txn) (*flow.ExecutionReceipt, error) { 105 retrievalOps := m.cache.Get(blockID) // assemble DB operations to retrieve receipt (no execution) 106 return func(tx *badger.Txn) (*flow.ExecutionReceipt, error) { 107 val, err := retrievalOps(tx) // execute operations to retrieve receipt 108 if err != nil { 109 return nil, err 110 } 111 return val.(*flow.ExecutionReceipt), nil 112 } 113 } 114 115 // StoreMyReceipt stores the receipt and marks it as mine (trusted). My 116 // receipts are indexed by the block whose result they compute. Currently, 117 // we only support indexing a _single_ receipt per block. Attempting to 118 // store conflicting receipts for the same block will error. 119 func (m *MyExecutionReceipts) StoreMyReceipt(receipt *flow.ExecutionReceipt) error { 120 return operation.RetryOnConflictTx(m.db, transaction.Update, m.storeMyReceipt(receipt)) 121 } 122 123 // BatchStoreMyReceipt stores blockID-to-my-receipt index entry keyed by blockID in a provided batch. 124 // No errors are expected during normal operation 125 // If entity fails marshalling, the error is wrapped in a generic error and returned. 126 // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned. 127 func (m *MyExecutionReceipts) BatchStoreMyReceipt(receipt *flow.ExecutionReceipt, batch storage.BatchStorage) error { 128 129 writeBatch := batch.GetWriter() 130 131 err := m.genericReceipts.BatchStore(receipt, batch) 132 if err != nil { 133 return fmt.Errorf("cannot batch store generic execution receipt inside my execution receipt batch store: %w", err) 134 } 135 136 err = operation.BatchIndexOwnExecutionReceipt(receipt.ExecutionResult.BlockID, receipt.ID())(writeBatch) 137 if err != nil { 138 return fmt.Errorf("cannot batch index own execution receipt inside my execution receipt batch store: %w", err) 139 } 140 141 return nil 142 } 143 144 // MyReceipt retrieves my receipt for the given block. 145 // Returns storage.ErrNotFound if no receipt was persisted for the block. 146 func (m *MyExecutionReceipts) MyReceipt(blockID flow.Identifier) (*flow.ExecutionReceipt, error) { 147 tx := m.db.NewTransaction(false) 148 defer tx.Discard() 149 return m.myReceipt(blockID)(tx) 150 } 151 152 func (m *MyExecutionReceipts) RemoveIndexByBlockID(blockID flow.Identifier) error { 153 return m.db.Update(operation.SkipNonExist(operation.RemoveOwnExecutionReceipt(blockID))) 154 } 155 156 // BatchRemoveIndexByBlockID removes blockID-to-my-execution-receipt index entry keyed by a blockID in a 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 (m *MyExecutionReceipts) BatchRemoveIndexByBlockID(blockID flow.Identifier, batch storage.BatchStorage) error { 160 writeBatch := batch.GetWriter() 161 return operation.BatchRemoveOwnExecutionReceipt(blockID)(writeBatch) 162 }