github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/chunkDataPacks.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  	badgermodel "github.com/onflow/flow-go/storage/badger/model"
    13  	"github.com/onflow/flow-go/storage/badger/operation"
    14  	"github.com/onflow/flow-go/storage/badger/transaction"
    15  )
    16  
    17  type ChunkDataPacks struct {
    18  	db             *badger.DB
    19  	collections    storage.Collections
    20  	byChunkIDCache *Cache[flow.Identifier, *badgermodel.StoredChunkDataPack]
    21  }
    22  
    23  func NewChunkDataPacks(collector module.CacheMetrics, db *badger.DB, collections storage.Collections, byChunkIDCacheSize uint) *ChunkDataPacks {
    24  
    25  	store := func(key flow.Identifier, val *badgermodel.StoredChunkDataPack) func(*transaction.Tx) error {
    26  		return transaction.WithTx(operation.SkipDuplicates(operation.InsertChunkDataPack(val)))
    27  	}
    28  
    29  	retrieve := func(key flow.Identifier) func(tx *badger.Txn) (*badgermodel.StoredChunkDataPack, error) {
    30  		return func(tx *badger.Txn) (*badgermodel.StoredChunkDataPack, error) {
    31  			var c badgermodel.StoredChunkDataPack
    32  			err := operation.RetrieveChunkDataPack(key, &c)(tx)
    33  			return &c, err
    34  		}
    35  	}
    36  
    37  	cache := newCache(collector, metrics.ResourceChunkDataPack,
    38  		withLimit[flow.Identifier, *badgermodel.StoredChunkDataPack](byChunkIDCacheSize),
    39  		withStore(store),
    40  		withRetrieve(retrieve),
    41  	)
    42  
    43  	ch := ChunkDataPacks{
    44  		db:             db,
    45  		byChunkIDCache: cache,
    46  		collections:    collections,
    47  	}
    48  	return &ch
    49  }
    50  
    51  // Remove removes multiple ChunkDataPacks cs keyed by their ChunkIDs in a batch.
    52  // No errors are expected during normal operation, even if no entries are matched.
    53  func (ch *ChunkDataPacks) Remove(chunkIDs []flow.Identifier) error {
    54  	batch := NewBatch(ch.db)
    55  
    56  	for _, c := range chunkIDs {
    57  		err := ch.BatchRemove(c, batch)
    58  		if err != nil {
    59  			return fmt.Errorf("cannot remove chunk data pack: %w", err)
    60  		}
    61  	}
    62  
    63  	err := batch.Flush()
    64  	if err != nil {
    65  		return fmt.Errorf("cannot flush batch to remove chunk data pack: %w", err)
    66  	}
    67  	return nil
    68  }
    69  
    70  // BatchStore stores ChunkDataPack c keyed by its ChunkID in provided batch.
    71  // No errors are expected during normal operation, but it may return generic error
    72  // if entity is not serializable or Badger unexpectedly fails to process request
    73  func (ch *ChunkDataPacks) BatchStore(c *flow.ChunkDataPack, batch storage.BatchStorage) error {
    74  	sc := toStoredChunkDataPack(c)
    75  	writeBatch := batch.GetWriter()
    76  	batch.OnSucceed(func() {
    77  		ch.byChunkIDCache.Insert(sc.ChunkID, sc)
    78  	})
    79  	return operation.BatchInsertChunkDataPack(sc)(writeBatch)
    80  }
    81  
    82  // Store stores multiple ChunkDataPacks cs keyed by their ChunkIDs in a batch.
    83  // No errors are expected during normal operation, but it may return generic error
    84  func (ch *ChunkDataPacks) Store(cs []*flow.ChunkDataPack) error {
    85  	batch := NewBatch(ch.db)
    86  	for _, c := range cs {
    87  		err := ch.BatchStore(c, batch)
    88  		if err != nil {
    89  			return fmt.Errorf("cannot store chunk data pack: %w", err)
    90  		}
    91  	}
    92  
    93  	err := batch.Flush()
    94  	if err != nil {
    95  		return fmt.Errorf("cannot flush batch: %w", err)
    96  	}
    97  	return nil
    98  }
    99  
   100  // BatchRemove removes ChunkDataPack c keyed by its ChunkID in provided batch
   101  // No errors are expected during normal operation, even if no entries are matched.
   102  // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned.
   103  func (ch *ChunkDataPacks) BatchRemove(chunkID flow.Identifier, batch storage.BatchStorage) error {
   104  	writeBatch := batch.GetWriter()
   105  	batch.OnSucceed(func() {
   106  		ch.byChunkIDCache.Remove(chunkID)
   107  	})
   108  	return operation.BatchRemoveChunkDataPack(chunkID)(writeBatch)
   109  }
   110  
   111  func (ch *ChunkDataPacks) ByChunkID(chunkID flow.Identifier) (*flow.ChunkDataPack, error) {
   112  	schdp, err := ch.byChunkID(chunkID)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	chdp := &flow.ChunkDataPack{
   118  		ChunkID:           schdp.ChunkID,
   119  		StartState:        schdp.StartState,
   120  		Proof:             schdp.Proof,
   121  		ExecutionDataRoot: schdp.ExecutionDataRoot,
   122  	}
   123  
   124  	if !schdp.SystemChunk {
   125  		collection, err := ch.collections.ByID(schdp.CollectionID)
   126  		if err != nil {
   127  			return nil, fmt.Errorf("could not retrive collection (id: %x) for stored chunk data pack: %w", schdp.CollectionID, err)
   128  		}
   129  
   130  		chdp.Collection = collection
   131  	}
   132  
   133  	return chdp, nil
   134  }
   135  
   136  func (ch *ChunkDataPacks) byChunkID(chunkID flow.Identifier) (*badgermodel.StoredChunkDataPack, error) {
   137  	tx := ch.db.NewTransaction(false)
   138  	defer tx.Discard()
   139  
   140  	schdp, err := ch.retrieveCHDP(chunkID)(tx)
   141  	if err != nil {
   142  		return nil, fmt.Errorf("could not retrive stored chunk data pack: %w", err)
   143  	}
   144  
   145  	return schdp, nil
   146  }
   147  
   148  func (ch *ChunkDataPacks) retrieveCHDP(chunkID flow.Identifier) func(*badger.Txn) (*badgermodel.StoredChunkDataPack, error) {
   149  	return func(tx *badger.Txn) (*badgermodel.StoredChunkDataPack, error) {
   150  		val, err := ch.byChunkIDCache.Get(chunkID)(tx)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  		return val, nil
   155  	}
   156  }
   157  
   158  func toStoredChunkDataPack(c *flow.ChunkDataPack) *badgermodel.StoredChunkDataPack {
   159  	sc := &badgermodel.StoredChunkDataPack{
   160  		ChunkID:           c.ChunkID,
   161  		StartState:        c.StartState,
   162  		Proof:             c.Proof,
   163  		SystemChunk:       false,
   164  		ExecutionDataRoot: c.ExecutionDataRoot,
   165  	}
   166  
   167  	if c.Collection != nil {
   168  		// non system chunk
   169  		sc.CollectionID = c.Collection.ID()
   170  	} else {
   171  		sc.SystemChunk = true
   172  	}
   173  
   174  	return sc
   175  }