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  }