github.com/koko1123/flow-go-1@v0.29.6/storage/badger/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 // ExecutionReceipts implements storage for execution receipts. 18 type ExecutionReceipts struct { 19 db *badger.DB 20 results *ExecutionResults 21 cache *Cache 22 } 23 24 // NewExecutionReceipts Creates ExecutionReceipts instance which is a database of receipts which 25 // supports storing and indexing receipts by receipt ID and block ID. 26 func NewExecutionReceipts(collector module.CacheMetrics, db *badger.DB, results *ExecutionResults, cacheSize uint) *ExecutionReceipts { 27 store := func(key interface{}, val interface{}) func(*transaction.Tx) error { 28 receipt := val.(*flow.ExecutionReceipt) 29 receiptID := receipt.ID() 30 31 // assemble DB operations to store result (no execution) 32 storeResultOps := results.store(&receipt.ExecutionResult) 33 // assemble DB operations to index receipt (no execution) 34 storeReceiptOps := transaction.WithTx(operation.SkipDuplicates(operation.InsertExecutionReceiptMeta(receiptID, receipt.Meta()))) 35 // assemble DB operations to index receipt by the block it computes (no execution) 36 indexReceiptOps := transaction.WithTx(operation.SkipDuplicates( 37 operation.IndexExecutionReceipts(receipt.ExecutionResult.BlockID, receiptID), 38 )) 39 40 return func(tx *transaction.Tx) error { 41 err := storeResultOps(tx) // execute operations to store results 42 if err != nil { 43 return fmt.Errorf("could not store result: %w", err) 44 } 45 err = storeReceiptOps(tx) // execute operations to store receipt-specific meta-data 46 if err != nil { 47 return fmt.Errorf("could not store receipt metadata: %w", err) 48 } 49 err = indexReceiptOps(tx) 50 if err != nil { 51 return fmt.Errorf("could not index receipt by the block it computes: %w", err) 52 } 53 return nil 54 } 55 } 56 57 retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { 58 receiptID := key.(flow.Identifier) 59 return func(tx *badger.Txn) (interface{}, error) { 60 var meta flow.ExecutionReceiptMeta 61 err := operation.RetrieveExecutionReceiptMeta(receiptID, &meta)(tx) 62 if err != nil { 63 return nil, fmt.Errorf("could not retrieve receipt meta: %w", err) 64 } 65 result, err := results.byID(meta.ResultID)(tx) 66 if err != nil { 67 return nil, fmt.Errorf("could not retrieve result: %w", err) 68 } 69 return flow.ExecutionReceiptFromMeta(meta, *result), nil 70 } 71 } 72 73 return &ExecutionReceipts{ 74 db: db, 75 results: results, 76 cache: newCache(collector, metrics.ResourceReceipt, 77 withLimit(cacheSize), 78 withStore(store), 79 withRetrieve(retrieve)), 80 } 81 } 82 83 // storeMyReceipt assembles the operations to store an arbitrary receipt. 84 func (r *ExecutionReceipts) storeTx(receipt *flow.ExecutionReceipt) func(*transaction.Tx) error { 85 return r.cache.PutTx(receipt.ID(), receipt) 86 } 87 88 func (r *ExecutionReceipts) byID(receiptID flow.Identifier) func(*badger.Txn) (*flow.ExecutionReceipt, error) { 89 retrievalOps := r.cache.Get(receiptID) // assemble DB operations to retrieve receipt (no execution) 90 return func(tx *badger.Txn) (*flow.ExecutionReceipt, error) { 91 val, err := retrievalOps(tx) // execute operations to retrieve receipt 92 if err != nil { 93 return nil, err 94 } 95 return val.(*flow.ExecutionReceipt), nil 96 } 97 } 98 99 func (r *ExecutionReceipts) byBlockID(blockID flow.Identifier) func(*badger.Txn) ([]*flow.ExecutionReceipt, error) { 100 return func(tx *badger.Txn) ([]*flow.ExecutionReceipt, error) { 101 var receiptIDs []flow.Identifier 102 err := operation.LookupExecutionReceipts(blockID, &receiptIDs)(tx) 103 if err != nil && !errors.Is(err, storage.ErrNotFound) { 104 return nil, fmt.Errorf("could not find receipt index for block: %w", err) 105 } 106 107 var receipts []*flow.ExecutionReceipt 108 for _, id := range receiptIDs { 109 receipt, err := r.byID(id)(tx) 110 if err != nil { 111 return nil, fmt.Errorf("could not find receipt with id %v: %w", id, err) 112 } 113 receipts = append(receipts, receipt) 114 } 115 return receipts, nil 116 } 117 } 118 119 func (r *ExecutionReceipts) Store(receipt *flow.ExecutionReceipt) error { 120 return operation.RetryOnConflictTx(r.db, transaction.Update, r.storeTx(receipt)) 121 } 122 123 func (r *ExecutionReceipts) BatchStore(receipt *flow.ExecutionReceipt, batch storage.BatchStorage) error { 124 writeBatch := batch.GetWriter() 125 126 err := r.results.BatchStore(&receipt.ExecutionResult, batch) 127 if err != nil { 128 return fmt.Errorf("cannot batch store execution result inside execution receipt batch store: %w", err) 129 } 130 131 err = operation.BatchInsertExecutionReceiptMeta(receipt.ID(), receipt.Meta())(writeBatch) 132 if err != nil { 133 return fmt.Errorf("cannot batch store execution meta inside execution receipt batch store: %w", err) 134 } 135 136 err = operation.BatchIndexExecutionReceipts(receipt.ExecutionResult.BlockID, receipt.ID())(writeBatch) 137 if err != nil { 138 return fmt.Errorf("cannot batch index execution receipt inside execution receipt batch store: %w", err) 139 } 140 141 return nil 142 } 143 144 func (r *ExecutionReceipts) ByID(receiptID flow.Identifier) (*flow.ExecutionReceipt, error) { 145 tx := r.db.NewTransaction(false) 146 defer tx.Discard() 147 return r.byID(receiptID)(tx) 148 } 149 150 func (r *ExecutionReceipts) ByBlockID(blockID flow.Identifier) (flow.ExecutionReceiptList, error) { 151 tx := r.db.NewTransaction(false) 152 defer tx.Discard() 153 return r.byBlockID(blockID)(tx) 154 }