github.com/koko1123/flow-go-1@v0.29.6/storage/badger/approvals.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 // ResultApprovals implements persistent storage for result approvals. 18 type ResultApprovals struct { 19 db *badger.DB 20 cache *Cache 21 } 22 23 func NewResultApprovals(collector module.CacheMetrics, db *badger.DB) *ResultApprovals { 24 25 store := func(key interface{}, val interface{}) func(*transaction.Tx) error { 26 approval := val.(*flow.ResultApproval) 27 return transaction.WithTx(operation.SkipDuplicates(operation.InsertResultApproval(approval))) 28 } 29 30 retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { 31 approvalID := key.(flow.Identifier) 32 var approval flow.ResultApproval 33 return func(tx *badger.Txn) (interface{}, error) { 34 err := operation.RetrieveResultApproval(approvalID, &approval)(tx) 35 return &approval, err 36 } 37 } 38 39 res := &ResultApprovals{ 40 db: db, 41 cache: newCache(collector, metrics.ResourceResultApprovals, 42 withLimit(flow.DefaultTransactionExpiry+100), 43 withStore(store), 44 withRetrieve(retrieve)), 45 } 46 47 return res 48 } 49 50 func (r *ResultApprovals) store(approval *flow.ResultApproval) func(*transaction.Tx) error { 51 return r.cache.PutTx(approval.ID(), approval) 52 } 53 54 func (r *ResultApprovals) byID(approvalID flow.Identifier) func(*badger.Txn) (*flow.ResultApproval, error) { 55 return func(tx *badger.Txn) (*flow.ResultApproval, error) { 56 val, err := r.cache.Get(approvalID)(tx) 57 if err != nil { 58 return nil, err 59 } 60 return val.(*flow.ResultApproval), nil 61 } 62 } 63 64 func (r *ResultApprovals) byChunk(resultID flow.Identifier, chunkIndex uint64) func(*badger.Txn) (*flow.ResultApproval, error) { 65 return func(tx *badger.Txn) (*flow.ResultApproval, error) { 66 var approvalID flow.Identifier 67 err := operation.LookupResultApproval(resultID, chunkIndex, &approvalID)(tx) 68 if err != nil { 69 return nil, fmt.Errorf("could not lookup result approval ID: %w", err) 70 } 71 return r.byID(approvalID)(tx) 72 } 73 } 74 75 func (r *ResultApprovals) index(resultID flow.Identifier, chunkIndex uint64, approvalID flow.Identifier) func(*badger.Txn) error { 76 return func(tx *badger.Txn) error { 77 err := operation.IndexResultApproval(resultID, chunkIndex, approvalID)(tx) 78 if err == nil { 79 return nil 80 } 81 82 if !errors.Is(err, storage.ErrAlreadyExists) { 83 return err 84 } 85 86 // When trying to index an approval for a result, and there is already 87 // an approval for the result, double check if the indexed approval is 88 // the same. 89 // We don't allow indexing multiple approvals per chunk because the 90 // store is only used within Verification nodes, and it is impossible 91 // for a Verification node to compute different approvals for the same 92 // chunk. 93 var storedApprovalID flow.Identifier 94 err = operation.LookupResultApproval(resultID, chunkIndex, &storedApprovalID)(tx) 95 if err != nil { 96 return fmt.Errorf("there is an approval stored already, but cannot retrieve it: %w", err) 97 } 98 99 if storedApprovalID != approvalID { 100 return fmt.Errorf("attempting to store conflicting approval (result: %v, chunk index: %d): storing: %v, stored: %v. %w", 101 resultID, chunkIndex, approvalID, storedApprovalID, storage.ErrDataMismatch) 102 } 103 104 return nil 105 } 106 } 107 108 // Store stores a ResultApproval 109 func (r *ResultApprovals) Store(approval *flow.ResultApproval) error { 110 return operation.RetryOnConflictTx(r.db, transaction.Update, r.store(approval)) 111 } 112 113 // Index indexes a ResultApproval by chunk (ResultID + chunk index). 114 // operation is idempotent (repeated calls with the same value are equivalent to 115 // just calling the method once; still the method succeeds on each call). 116 func (r *ResultApprovals) Index(resultID flow.Identifier, chunkIndex uint64, approvalID flow.Identifier) error { 117 err := operation.RetryOnConflict(r.db.Update, r.index(resultID, chunkIndex, approvalID)) 118 if err != nil { 119 return fmt.Errorf("could not index result approval: %w", err) 120 } 121 return nil 122 } 123 124 // ByID retrieves a ResultApproval by its ID 125 func (r *ResultApprovals) ByID(approvalID flow.Identifier) (*flow.ResultApproval, error) { 126 tx := r.db.NewTransaction(false) 127 defer tx.Discard() 128 return r.byID(approvalID)(tx) 129 } 130 131 // ByChunk retrieves a ResultApproval by result ID and chunk index. The 132 // ResultApprovals store is only used within a verification node, where it is 133 // assumed that there is never more than one approval per chunk. 134 func (r *ResultApprovals) ByChunk(resultID flow.Identifier, chunkIndex uint64) (*flow.ResultApproval, error) { 135 tx := r.db.NewTransaction(false) 136 defer tx.Discard() 137 return r.byChunk(resultID, chunkIndex)(tx) 138 }