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  }