github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/seals.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/badger/operation"
    12  	"github.com/onflow/flow-go/storage/badger/transaction"
    13  )
    14  
    15  type Seals struct {
    16  	db    *badger.DB
    17  	cache *Cache[flow.Identifier, *flow.Seal]
    18  }
    19  
    20  func NewSeals(collector module.CacheMetrics, db *badger.DB) *Seals {
    21  
    22  	store := func(sealID flow.Identifier, seal *flow.Seal) func(*transaction.Tx) error {
    23  		return transaction.WithTx(operation.SkipDuplicates(operation.InsertSeal(sealID, seal)))
    24  	}
    25  
    26  	retrieve := func(sealID flow.Identifier) func(*badger.Txn) (*flow.Seal, error) {
    27  		return func(tx *badger.Txn) (*flow.Seal, error) {
    28  			var seal flow.Seal
    29  			err := operation.RetrieveSeal(sealID, &seal)(tx)
    30  			return &seal, err
    31  		}
    32  	}
    33  
    34  	s := &Seals{
    35  		db: db,
    36  		cache: newCache[flow.Identifier, *flow.Seal](collector, metrics.ResourceSeal,
    37  			withLimit[flow.Identifier, *flow.Seal](flow.DefaultTransactionExpiry+100),
    38  			withStore(store),
    39  			withRetrieve(retrieve)),
    40  	}
    41  
    42  	return s
    43  }
    44  
    45  func (s *Seals) storeTx(seal *flow.Seal) func(*transaction.Tx) error {
    46  	return s.cache.PutTx(seal.ID(), seal)
    47  }
    48  
    49  func (s *Seals) retrieveTx(sealID flow.Identifier) func(*badger.Txn) (*flow.Seal, error) {
    50  	return func(tx *badger.Txn) (*flow.Seal, error) {
    51  		val, err := s.cache.Get(sealID)(tx)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		return val, err
    56  	}
    57  }
    58  
    59  func (s *Seals) Store(seal *flow.Seal) error {
    60  	return operation.RetryOnConflictTx(s.db, transaction.Update, s.storeTx(seal))
    61  }
    62  
    63  func (s *Seals) ByID(sealID flow.Identifier) (*flow.Seal, error) {
    64  	tx := s.db.NewTransaction(false)
    65  	defer tx.Discard()
    66  	return s.retrieveTx(sealID)(tx)
    67  }
    68  
    69  // HighestInFork retrieves the highest seal that was included in the
    70  // fork up to (and including) blockID. This method should return a seal
    71  // for any block known to the node. Returns storage.ErrNotFound if
    72  // blockID is unknown.
    73  func (s *Seals) HighestInFork(blockID flow.Identifier) (*flow.Seal, error) {
    74  	var sealID flow.Identifier
    75  	err := s.db.View(operation.LookupLatestSealAtBlock(blockID, &sealID))
    76  	if err != nil {
    77  		return nil, fmt.Errorf("failed to retrieve seal for fork with head %x: %w", blockID, err)
    78  	}
    79  	return s.ByID(sealID)
    80  }
    81  
    82  // FinalizedSealForBlock returns the seal for the given block, only if that seal
    83  // has been included in a finalized block.
    84  // Returns storage.ErrNotFound if the block is unknown or unsealed.
    85  func (s *Seals) FinalizedSealForBlock(blockID flow.Identifier) (*flow.Seal, error) {
    86  	var sealID flow.Identifier
    87  	err := s.db.View(operation.LookupBySealedBlockID(blockID, &sealID))
    88  	if err != nil {
    89  		return nil, fmt.Errorf("failed to retrieve seal for block %x: %w", blockID, err)
    90  	}
    91  	return s.ByID(sealID)
    92  }