github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/light_transaction_results.go (about)

     1  package badger
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/dgraph-io/badger/v2"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/module"
    10  	"github.com/onflow/flow-go/module/metrics"
    11  	"github.com/onflow/flow-go/storage"
    12  	"github.com/onflow/flow-go/storage/badger/operation"
    13  )
    14  
    15  var _ storage.LightTransactionResults = (*LightTransactionResults)(nil)
    16  
    17  type LightTransactionResults struct {
    18  	db         *badger.DB
    19  	cache      *Cache[string, flow.LightTransactionResult]
    20  	indexCache *Cache[string, flow.LightTransactionResult]
    21  	blockCache *Cache[string, []flow.LightTransactionResult]
    22  }
    23  
    24  func NewLightTransactionResults(collector module.CacheMetrics, db *badger.DB, transactionResultsCacheSize uint) *LightTransactionResults {
    25  	retrieve := func(key string) func(tx *badger.Txn) (flow.LightTransactionResult, error) {
    26  		var txResult flow.LightTransactionResult
    27  		return func(tx *badger.Txn) (flow.LightTransactionResult, error) {
    28  
    29  			blockID, txID, err := KeyToBlockIDTransactionID(key)
    30  			if err != nil {
    31  				return flow.LightTransactionResult{}, fmt.Errorf("could not convert key: %w", err)
    32  			}
    33  
    34  			err = operation.RetrieveLightTransactionResult(blockID, txID, &txResult)(tx)
    35  			if err != nil {
    36  				return flow.LightTransactionResult{}, handleError(err, flow.LightTransactionResult{})
    37  			}
    38  			return txResult, nil
    39  		}
    40  	}
    41  	retrieveIndex := func(key string) func(tx *badger.Txn) (flow.LightTransactionResult, error) {
    42  		var txResult flow.LightTransactionResult
    43  		return func(tx *badger.Txn) (flow.LightTransactionResult, error) {
    44  
    45  			blockID, txIndex, err := KeyToBlockIDIndex(key)
    46  			if err != nil {
    47  				return flow.LightTransactionResult{}, fmt.Errorf("could not convert index key: %w", err)
    48  			}
    49  
    50  			err = operation.RetrieveLightTransactionResultByIndex(blockID, txIndex, &txResult)(tx)
    51  			if err != nil {
    52  				return flow.LightTransactionResult{}, handleError(err, flow.LightTransactionResult{})
    53  			}
    54  			return txResult, nil
    55  		}
    56  	}
    57  	retrieveForBlock := func(key string) func(tx *badger.Txn) ([]flow.LightTransactionResult, error) {
    58  		var txResults []flow.LightTransactionResult
    59  		return func(tx *badger.Txn) ([]flow.LightTransactionResult, error) {
    60  
    61  			blockID, err := KeyToBlockID(key)
    62  			if err != nil {
    63  				return nil, fmt.Errorf("could not convert index key: %w", err)
    64  			}
    65  
    66  			err = operation.LookupLightTransactionResultsByBlockIDUsingIndex(blockID, &txResults)(tx)
    67  			if err != nil {
    68  				return nil, handleError(err, flow.LightTransactionResult{})
    69  			}
    70  			return txResults, nil
    71  		}
    72  	}
    73  	return &LightTransactionResults{
    74  		db: db,
    75  		cache: newCache[string, flow.LightTransactionResult](collector, metrics.ResourceTransactionResults,
    76  			withLimit[string, flow.LightTransactionResult](transactionResultsCacheSize),
    77  			withStore(noopStore[string, flow.LightTransactionResult]),
    78  			withRetrieve(retrieve),
    79  		),
    80  		indexCache: newCache[string, flow.LightTransactionResult](collector, metrics.ResourceTransactionResultIndices,
    81  			withLimit[string, flow.LightTransactionResult](transactionResultsCacheSize),
    82  			withStore(noopStore[string, flow.LightTransactionResult]),
    83  			withRetrieve(retrieveIndex),
    84  		),
    85  		blockCache: newCache[string, []flow.LightTransactionResult](collector, metrics.ResourceTransactionResultIndices,
    86  			withLimit[string, []flow.LightTransactionResult](transactionResultsCacheSize),
    87  			withStore(noopStore[string, []flow.LightTransactionResult]),
    88  			withRetrieve(retrieveForBlock),
    89  		),
    90  	}
    91  }
    92  
    93  func (tr *LightTransactionResults) BatchStore(blockID flow.Identifier, transactionResults []flow.LightTransactionResult, batch storage.BatchStorage) error {
    94  	writeBatch := batch.GetWriter()
    95  
    96  	for i, result := range transactionResults {
    97  		err := operation.BatchInsertLightTransactionResult(blockID, &result)(writeBatch)
    98  		if err != nil {
    99  			return fmt.Errorf("cannot batch insert tx result: %w", err)
   100  		}
   101  
   102  		err = operation.BatchIndexLightTransactionResult(blockID, uint32(i), &result)(writeBatch)
   103  		if err != nil {
   104  			return fmt.Errorf("cannot batch index tx result: %w", err)
   105  		}
   106  	}
   107  
   108  	batch.OnSucceed(func() {
   109  		for i, result := range transactionResults {
   110  			key := KeyFromBlockIDTransactionID(blockID, result.TransactionID)
   111  			// cache for each transaction, so that it's faster to retrieve
   112  			tr.cache.Insert(key, result)
   113  
   114  			index := uint32(i)
   115  
   116  			keyIndex := KeyFromBlockIDIndex(blockID, index)
   117  			tr.indexCache.Insert(keyIndex, result)
   118  		}
   119  
   120  		key := KeyFromBlockID(blockID)
   121  		tr.blockCache.Insert(key, transactionResults)
   122  	})
   123  	return nil
   124  }
   125  
   126  // ByBlockIDTransactionID returns the transaction result for the given block ID and transaction ID
   127  func (tr *LightTransactionResults) ByBlockIDTransactionID(blockID flow.Identifier, txID flow.Identifier) (*flow.LightTransactionResult, error) {
   128  	tx := tr.db.NewTransaction(false)
   129  	defer tx.Discard()
   130  	key := KeyFromBlockIDTransactionID(blockID, txID)
   131  	transactionResult, err := tr.cache.Get(key)(tx)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	return &transactionResult, nil
   136  }
   137  
   138  // ByBlockIDTransactionIndex returns the transaction result for the given blockID and transaction index
   139  func (tr *LightTransactionResults) ByBlockIDTransactionIndex(blockID flow.Identifier, txIndex uint32) (*flow.LightTransactionResult, error) {
   140  	tx := tr.db.NewTransaction(false)
   141  	defer tx.Discard()
   142  	key := KeyFromBlockIDIndex(blockID, txIndex)
   143  	transactionResult, err := tr.indexCache.Get(key)(tx)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	return &transactionResult, nil
   148  }
   149  
   150  // ByBlockID gets all transaction results for a block, ordered by transaction index
   151  func (tr *LightTransactionResults) ByBlockID(blockID flow.Identifier) ([]flow.LightTransactionResult, error) {
   152  	tx := tr.db.NewTransaction(false)
   153  	defer tx.Discard()
   154  	key := KeyFromBlockID(blockID)
   155  	transactionResults, err := tr.blockCache.Get(key)(tx)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	return transactionResults, nil
   160  }