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 }