github.com/koko1123/flow-go-1@v0.29.6/storage/badger/my_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  // MyExecutionReceipts holds and indexes Execution Receipts.
    18  // MyExecutionReceipts is implemented as a wrapper around badger.ExecutionReceipts
    19  // The wrapper adds the ability to "MY execution receipt", from the viewpoint
    20  // of an individual Execution Node.
    21  type MyExecutionReceipts struct {
    22  	genericReceipts *ExecutionReceipts
    23  	db              *badger.DB
    24  	cache           *Cache
    25  }
    26  
    27  // NewMyExecutionReceipts creates instance of MyExecutionReceipts which is a wrapper wrapper around badger.ExecutionReceipts
    28  // It's useful for execution nodes to keep track of produced execution receipts.
    29  func NewMyExecutionReceipts(collector module.CacheMetrics, db *badger.DB, receipts *ExecutionReceipts) *MyExecutionReceipts {
    30  	store := func(key interface{}, val interface{}) func(*transaction.Tx) error {
    31  		receipt := val.(*flow.ExecutionReceipt)
    32  		// assemble DB operations to store receipt (no execution)
    33  		storeReceiptOps := receipts.storeTx(receipt)
    34  		// assemble DB operations to index receipt as one of my own (no execution)
    35  		blockID := receipt.ExecutionResult.BlockID
    36  		receiptID := receipt.ID()
    37  		indexOwnReceiptOps := transaction.WithTx(func(tx *badger.Txn) error {
    38  			err := operation.IndexOwnExecutionReceipt(blockID, receiptID)(tx)
    39  			// check if we are storing same receipt
    40  			if errors.Is(err, storage.ErrAlreadyExists) {
    41  				var savedReceiptID flow.Identifier
    42  				err := operation.LookupOwnExecutionReceipt(blockID, &savedReceiptID)(tx)
    43  				if err != nil {
    44  					return err
    45  				}
    46  
    47  				if savedReceiptID == receiptID {
    48  					// if we are storing same receipt we shouldn't error
    49  					return nil
    50  				}
    51  
    52  				return fmt.Errorf("indexing my receipt %v failed: different receipt %v for the same block %v is already indexed", receiptID,
    53  					savedReceiptID, blockID)
    54  			}
    55  			return err
    56  		})
    57  
    58  		return func(tx *transaction.Tx) error {
    59  			err := storeReceiptOps(tx) // execute operations to store receipt
    60  			if err != nil {
    61  				return fmt.Errorf("could not store receipt: %w", err)
    62  			}
    63  			err = indexOwnReceiptOps(tx) // execute operations to index receipt as one of my own
    64  			if err != nil {
    65  				return fmt.Errorf("could not index receipt as one of my own: %w", err)
    66  			}
    67  			return nil
    68  		}
    69  	}
    70  
    71  	retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) {
    72  		blockID := key.(flow.Identifier)
    73  
    74  		return func(tx *badger.Txn) (interface{}, error) {
    75  			var receiptID flow.Identifier
    76  			err := operation.LookupOwnExecutionReceipt(blockID, &receiptID)(tx)
    77  			if err != nil {
    78  				return nil, fmt.Errorf("could not lookup receipt ID: %w", err)
    79  			}
    80  			receipt, err := receipts.byID(receiptID)(tx)
    81  			if err != nil {
    82  				return nil, err
    83  			}
    84  			return receipt, nil
    85  		}
    86  	}
    87  
    88  	return &MyExecutionReceipts{
    89  		genericReceipts: receipts,
    90  		db:              db,
    91  		cache: newCache(collector, metrics.ResourceMyReceipt,
    92  			withLimit(flow.DefaultTransactionExpiry+100),
    93  			withStore(store),
    94  			withRetrieve(retrieve)),
    95  	}
    96  }
    97  
    98  // storeMyReceipt assembles the operations to store the receipt and marks it as mine (trusted).
    99  func (m *MyExecutionReceipts) storeMyReceipt(receipt *flow.ExecutionReceipt) func(*transaction.Tx) error {
   100  	return m.cache.PutTx(receipt.ExecutionResult.BlockID, receipt)
   101  }
   102  
   103  // storeMyReceipt assembles the operations to retrieve my receipt for the given block ID.
   104  func (m *MyExecutionReceipts) myReceipt(blockID flow.Identifier) func(*badger.Txn) (*flow.ExecutionReceipt, error) {
   105  	retrievalOps := m.cache.Get(blockID) // assemble DB operations to retrieve receipt (no execution)
   106  	return func(tx *badger.Txn) (*flow.ExecutionReceipt, error) {
   107  		val, err := retrievalOps(tx) // execute operations to retrieve receipt
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		return val.(*flow.ExecutionReceipt), nil
   112  	}
   113  }
   114  
   115  // StoreMyReceipt stores the receipt and marks it as mine (trusted). My
   116  // receipts are indexed by the block whose result they compute. Currently,
   117  // we only support indexing a _single_ receipt per block. Attempting to
   118  // store conflicting receipts for the same block will error.
   119  func (m *MyExecutionReceipts) StoreMyReceipt(receipt *flow.ExecutionReceipt) error {
   120  	return operation.RetryOnConflictTx(m.db, transaction.Update, m.storeMyReceipt(receipt))
   121  }
   122  
   123  // BatchStoreMyReceipt stores blockID-to-my-receipt index entry keyed by blockID in a provided batch.
   124  // No errors are expected during normal operation
   125  // If entity fails marshalling, the error is wrapped in a generic error and returned.
   126  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   127  func (m *MyExecutionReceipts) BatchStoreMyReceipt(receipt *flow.ExecutionReceipt, batch storage.BatchStorage) error {
   128  
   129  	writeBatch := batch.GetWriter()
   130  
   131  	err := m.genericReceipts.BatchStore(receipt, batch)
   132  	if err != nil {
   133  		return fmt.Errorf("cannot batch store generic execution receipt inside my execution receipt batch store: %w", err)
   134  	}
   135  
   136  	err = operation.BatchIndexOwnExecutionReceipt(receipt.ExecutionResult.BlockID, receipt.ID())(writeBatch)
   137  	if err != nil {
   138  		return fmt.Errorf("cannot batch index own execution receipt inside my execution receipt batch store: %w", err)
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  // MyReceipt retrieves my receipt for the given block.
   145  // Returns storage.ErrNotFound if no receipt was persisted for the block.
   146  func (m *MyExecutionReceipts) MyReceipt(blockID flow.Identifier) (*flow.ExecutionReceipt, error) {
   147  	tx := m.db.NewTransaction(false)
   148  	defer tx.Discard()
   149  	return m.myReceipt(blockID)(tx)
   150  }
   151  
   152  func (m *MyExecutionReceipts) RemoveIndexByBlockID(blockID flow.Identifier) error {
   153  	return m.db.Update(operation.SkipNonExist(operation.RemoveOwnExecutionReceipt(blockID)))
   154  }
   155  
   156  // BatchRemoveIndexByBlockID removes blockID-to-my-execution-receipt index entry keyed by a blockID in a provided batch
   157  // No errors are expected during normal operation, even if no entries are matched.
   158  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   159  func (m *MyExecutionReceipts) BatchRemoveIndexByBlockID(blockID flow.Identifier, batch storage.BatchStorage) error {
   160  	writeBatch := batch.GetWriter()
   161  	return operation.BatchRemoveOwnExecutionReceipt(blockID)(writeBatch)
   162  }