github.com/koko1123/flow-go-1@v0.29.6/storage/badger/results.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 // ExecutionResults implements persistent storage for execution results. 18 type ExecutionResults struct { 19 db *badger.DB 20 cache *Cache 21 } 22 23 var _ storage.ExecutionResults = (*ExecutionResults)(nil) 24 25 func NewExecutionResults(collector module.CacheMetrics, db *badger.DB) *ExecutionResults { 26 27 store := func(key interface{}, val interface{}) func(*transaction.Tx) error { 28 result := val.(*flow.ExecutionResult) 29 return transaction.WithTx(operation.SkipDuplicates(operation.InsertExecutionResult(result))) 30 } 31 32 retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { 33 resultID := key.(flow.Identifier) 34 var result flow.ExecutionResult 35 return func(tx *badger.Txn) (interface{}, error) { 36 err := operation.RetrieveExecutionResult(resultID, &result)(tx) 37 return &result, err 38 } 39 } 40 41 res := &ExecutionResults{ 42 db: db, 43 cache: newCache(collector, metrics.ResourceResult, 44 withLimit(flow.DefaultTransactionExpiry+100), 45 withStore(store), 46 withRetrieve(retrieve)), 47 } 48 49 return res 50 } 51 52 func (r *ExecutionResults) store(result *flow.ExecutionResult) func(*transaction.Tx) error { 53 return r.cache.PutTx(result.ID(), result) 54 } 55 56 func (r *ExecutionResults) byID(resultID flow.Identifier) func(*badger.Txn) (*flow.ExecutionResult, error) { 57 return func(tx *badger.Txn) (*flow.ExecutionResult, error) { 58 val, err := r.cache.Get(resultID)(tx) 59 if err != nil { 60 return nil, err 61 } 62 return val.(*flow.ExecutionResult), nil 63 } 64 } 65 66 func (r *ExecutionResults) byBlockID(blockID flow.Identifier) func(*badger.Txn) (*flow.ExecutionResult, error) { 67 return func(tx *badger.Txn) (*flow.ExecutionResult, error) { 68 var resultID flow.Identifier 69 err := operation.LookupExecutionResult(blockID, &resultID)(tx) 70 if err != nil { 71 return nil, fmt.Errorf("could not lookup execution result ID: %w", err) 72 } 73 return r.byID(resultID)(tx) 74 } 75 } 76 77 func (r *ExecutionResults) index(blockID, resultID flow.Identifier, force bool) func(*transaction.Tx) error { 78 return func(tx *transaction.Tx) error { 79 err := transaction.WithTx(operation.IndexExecutionResult(blockID, resultID))(tx) 80 if err == nil { 81 return nil 82 } 83 84 if !errors.Is(err, storage.ErrAlreadyExists) { 85 return err 86 } 87 88 if force { 89 return transaction.WithTx(operation.ReindexExecutionResult(blockID, resultID))(tx) 90 } 91 92 // when trying to index a result for a block, and there is already a result indexed for this block, 93 // double check if the indexed result is the same 94 var storedResultID flow.Identifier 95 err = transaction.WithTx(operation.LookupExecutionResult(blockID, &storedResultID))(tx) 96 if err != nil { 97 return fmt.Errorf("there is a result stored already, but cannot retrieve it: %w", err) 98 } 99 100 if storedResultID != resultID { 101 return fmt.Errorf("storing result that is different from the already stored one for block: %v, storing result: %v, stored result: %v. %w", 102 blockID, resultID, storedResultID, storage.ErrDataMismatch) 103 } 104 105 return nil 106 } 107 } 108 109 func (r *ExecutionResults) Store(result *flow.ExecutionResult) error { 110 return operation.RetryOnConflictTx(r.db, transaction.Update, r.store(result)) 111 } 112 113 func (r *ExecutionResults) BatchStore(result *flow.ExecutionResult, batch storage.BatchStorage) error { 114 writeBatch := batch.GetWriter() 115 return operation.BatchInsertExecutionResult(result)(writeBatch) 116 } 117 118 func (r *ExecutionResults) BatchIndex(blockID flow.Identifier, resultID flow.Identifier, batch storage.BatchStorage) error { 119 writeBatch := batch.GetWriter() 120 return operation.BatchIndexExecutionResult(blockID, resultID)(writeBatch) 121 } 122 123 func (r *ExecutionResults) ByID(resultID flow.Identifier) (*flow.ExecutionResult, error) { 124 tx := r.db.NewTransaction(false) 125 defer tx.Discard() 126 return r.byID(resultID)(tx) 127 } 128 129 func (r *ExecutionResults) ByIDTx(resultID flow.Identifier) func(*transaction.Tx) (*flow.ExecutionResult, error) { 130 return func(tx *transaction.Tx) (*flow.ExecutionResult, error) { 131 result, err := r.byID(resultID)(tx.DBTxn) 132 return result, err 133 } 134 } 135 136 func (r *ExecutionResults) Index(blockID flow.Identifier, resultID flow.Identifier) error { 137 err := operation.RetryOnConflictTx(r.db, transaction.Update, r.index(blockID, resultID, false)) 138 if err != nil { 139 return fmt.Errorf("could not index execution result: %w", err) 140 } 141 return nil 142 } 143 144 func (r *ExecutionResults) ForceIndex(blockID flow.Identifier, resultID flow.Identifier) error { 145 err := operation.RetryOnConflictTx(r.db, transaction.Update, r.index(blockID, resultID, true)) 146 if err != nil { 147 return fmt.Errorf("could not index execution result: %w", err) 148 } 149 return nil 150 } 151 152 func (r *ExecutionResults) ByBlockID(blockID flow.Identifier) (*flow.ExecutionResult, error) { 153 tx := r.db.NewTransaction(false) 154 defer tx.Discard() 155 return r.byBlockID(blockID)(tx) 156 } 157 158 func (r *ExecutionResults) RemoveIndexByBlockID(blockID flow.Identifier) error { 159 return r.db.Update(operation.SkipNonExist(operation.RemoveExecutionResultIndex(blockID))) 160 } 161 162 // BatchRemoveIndexByBlockID removes blockID-to-executionResultID index entries keyed by blockID in a provided batch. 163 // No errors are expected during normal operation, even if no entries are matched. 164 // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned. 165 func (r *ExecutionResults) BatchRemoveIndexByBlockID(blockID flow.Identifier, batch storage.BatchStorage) error { 166 writeBatch := batch.GetWriter() 167 return operation.BatchRemoveExecutionResultIndex(blockID)(writeBatch) 168 }