github.com/koko1123/flow-go-1@v0.29.6/storage/badger/results.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  // ExecutionResults implements persistent storage for execution results.
    18  type ExecutionResults struct {
    19  	db    *badger.DB
    20  	cache *Cache
    21  }
    22  
    23  var _ storage.ExecutionResults = (*ExecutionResults)(nil)
    24  
    25  func NewExecutionResults(collector module.CacheMetrics, db *badger.DB) *ExecutionResults {
    26  
    27  	store := func(key interface{}, val interface{}) func(*transaction.Tx) error {
    28  		result := val.(*flow.ExecutionResult)
    29  		return transaction.WithTx(operation.SkipDuplicates(operation.InsertExecutionResult(result)))
    30  	}
    31  
    32  	retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) {
    33  		resultID := key.(flow.Identifier)
    34  		var result flow.ExecutionResult
    35  		return func(tx *badger.Txn) (interface{}, error) {
    36  			err := operation.RetrieveExecutionResult(resultID, &result)(tx)
    37  			return &result, err
    38  		}
    39  	}
    40  
    41  	res := &ExecutionResults{
    42  		db: db,
    43  		cache: newCache(collector, metrics.ResourceResult,
    44  			withLimit(flow.DefaultTransactionExpiry+100),
    45  			withStore(store),
    46  			withRetrieve(retrieve)),
    47  	}
    48  
    49  	return res
    50  }
    51  
    52  func (r *ExecutionResults) store(result *flow.ExecutionResult) func(*transaction.Tx) error {
    53  	return r.cache.PutTx(result.ID(), result)
    54  }
    55  
    56  func (r *ExecutionResults) byID(resultID flow.Identifier) func(*badger.Txn) (*flow.ExecutionResult, error) {
    57  	return func(tx *badger.Txn) (*flow.ExecutionResult, error) {
    58  		val, err := r.cache.Get(resultID)(tx)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  		return val.(*flow.ExecutionResult), nil
    63  	}
    64  }
    65  
    66  func (r *ExecutionResults) byBlockID(blockID flow.Identifier) func(*badger.Txn) (*flow.ExecutionResult, error) {
    67  	return func(tx *badger.Txn) (*flow.ExecutionResult, error) {
    68  		var resultID flow.Identifier
    69  		err := operation.LookupExecutionResult(blockID, &resultID)(tx)
    70  		if err != nil {
    71  			return nil, fmt.Errorf("could not lookup execution result ID: %w", err)
    72  		}
    73  		return r.byID(resultID)(tx)
    74  	}
    75  }
    76  
    77  func (r *ExecutionResults) index(blockID, resultID flow.Identifier, force bool) func(*transaction.Tx) error {
    78  	return func(tx *transaction.Tx) error {
    79  		err := transaction.WithTx(operation.IndexExecutionResult(blockID, resultID))(tx)
    80  		if err == nil {
    81  			return nil
    82  		}
    83  
    84  		if !errors.Is(err, storage.ErrAlreadyExists) {
    85  			return err
    86  		}
    87  
    88  		if force {
    89  			return transaction.WithTx(operation.ReindexExecutionResult(blockID, resultID))(tx)
    90  		}
    91  
    92  		// when trying to index a result for a block, and there is already a result indexed for this block,
    93  		// double check if the indexed result is the same
    94  		var storedResultID flow.Identifier
    95  		err = transaction.WithTx(operation.LookupExecutionResult(blockID, &storedResultID))(tx)
    96  		if err != nil {
    97  			return fmt.Errorf("there is a result stored already, but cannot retrieve it: %w", err)
    98  		}
    99  
   100  		if storedResultID != resultID {
   101  			return fmt.Errorf("storing result that is different from the already stored one for block: %v, storing result: %v, stored result: %v. %w",
   102  				blockID, resultID, storedResultID, storage.ErrDataMismatch)
   103  		}
   104  
   105  		return nil
   106  	}
   107  }
   108  
   109  func (r *ExecutionResults) Store(result *flow.ExecutionResult) error {
   110  	return operation.RetryOnConflictTx(r.db, transaction.Update, r.store(result))
   111  }
   112  
   113  func (r *ExecutionResults) BatchStore(result *flow.ExecutionResult, batch storage.BatchStorage) error {
   114  	writeBatch := batch.GetWriter()
   115  	return operation.BatchInsertExecutionResult(result)(writeBatch)
   116  }
   117  
   118  func (r *ExecutionResults) BatchIndex(blockID flow.Identifier, resultID flow.Identifier, batch storage.BatchStorage) error {
   119  	writeBatch := batch.GetWriter()
   120  	return operation.BatchIndexExecutionResult(blockID, resultID)(writeBatch)
   121  }
   122  
   123  func (r *ExecutionResults) ByID(resultID flow.Identifier) (*flow.ExecutionResult, error) {
   124  	tx := r.db.NewTransaction(false)
   125  	defer tx.Discard()
   126  	return r.byID(resultID)(tx)
   127  }
   128  
   129  func (r *ExecutionResults) ByIDTx(resultID flow.Identifier) func(*transaction.Tx) (*flow.ExecutionResult, error) {
   130  	return func(tx *transaction.Tx) (*flow.ExecutionResult, error) {
   131  		result, err := r.byID(resultID)(tx.DBTxn)
   132  		return result, err
   133  	}
   134  }
   135  
   136  func (r *ExecutionResults) Index(blockID flow.Identifier, resultID flow.Identifier) error {
   137  	err := operation.RetryOnConflictTx(r.db, transaction.Update, r.index(blockID, resultID, false))
   138  	if err != nil {
   139  		return fmt.Errorf("could not index execution result: %w", err)
   140  	}
   141  	return nil
   142  }
   143  
   144  func (r *ExecutionResults) ForceIndex(blockID flow.Identifier, resultID flow.Identifier) error {
   145  	err := operation.RetryOnConflictTx(r.db, transaction.Update, r.index(blockID, resultID, true))
   146  	if err != nil {
   147  		return fmt.Errorf("could not index execution result: %w", err)
   148  	}
   149  	return nil
   150  }
   151  
   152  func (r *ExecutionResults) ByBlockID(blockID flow.Identifier) (*flow.ExecutionResult, error) {
   153  	tx := r.db.NewTransaction(false)
   154  	defer tx.Discard()
   155  	return r.byBlockID(blockID)(tx)
   156  }
   157  
   158  func (r *ExecutionResults) RemoveIndexByBlockID(blockID flow.Identifier) error {
   159  	return r.db.Update(operation.SkipNonExist(operation.RemoveExecutionResultIndex(blockID)))
   160  }
   161  
   162  // BatchRemoveIndexByBlockID removes blockID-to-executionResultID index entries keyed by blockID in a provided batch.
   163  // No errors are expected during normal operation, even if no entries are matched.
   164  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   165  func (r *ExecutionResults) BatchRemoveIndexByBlockID(blockID flow.Identifier, batch storage.BatchStorage) error {
   166  	writeBatch := batch.GetWriter()
   167  	return operation.BatchRemoveExecutionResultIndex(blockID)(writeBatch)
   168  }