github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/chunk_data_pack_test.go (about) 1 package badger_test 2 3 import ( 4 "errors" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/dgraph-io/badger/v2" 10 11 "github.com/onflow/flow-go/model/flow" 12 "github.com/onflow/flow-go/module/metrics" 13 "github.com/onflow/flow-go/storage" 14 badgerstorage "github.com/onflow/flow-go/storage/badger" 15 "github.com/onflow/flow-go/utils/unittest" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 // TestChunkDataPacks_Store evaluates correct storage and retrieval of chunk data packs in the storage. 22 // It also evaluates that re-inserting is idempotent. 23 func TestChunkDataPacks_Store(t *testing.T) { 24 WithChunkDataPacks(t, 100, func(t *testing.T, chunkDataPacks []*flow.ChunkDataPack, chunkDataPackStore *badgerstorage.ChunkDataPacks, _ *badger.DB) { 25 require.NoError(t, chunkDataPackStore.Store(chunkDataPacks)) 26 require.NoError(t, chunkDataPackStore.Store(chunkDataPacks)) 27 }) 28 } 29 30 func TestChunkDataPack_Remove(t *testing.T) { 31 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 32 transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) 33 collections := badgerstorage.NewCollections(db, transactions) 34 // keep the cache size at 1 to make sure that entries are written and read from storage itself. 35 chunkDataPackStore := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) 36 37 chunkDataPacks := unittest.ChunkDataPacksFixture(10) 38 for _, chunkDataPack := range chunkDataPacks { 39 // stores collection in Collections storage (which ChunkDataPacks store uses internally) 40 err := collections.Store(chunkDataPack.Collection) 41 require.NoError(t, err) 42 } 43 44 chunkIDs := make([]flow.Identifier, 0, len(chunkDataPacks)) 45 for _, chunk := range chunkDataPacks { 46 chunkIDs = append(chunkIDs, chunk.ID()) 47 } 48 49 require.NoError(t, chunkDataPackStore.Store(chunkDataPacks)) 50 require.NoError(t, chunkDataPackStore.Remove(chunkIDs)) 51 52 // verify it has been removed 53 _, err := chunkDataPackStore.ByChunkID(chunkIDs[0]) 54 assert.True(t, errors.Is(err, storage.ErrNotFound)) 55 56 // Removing again should not error 57 require.NoError(t, chunkDataPackStore.Remove(chunkIDs)) 58 }) 59 } 60 61 // TestChunkDataPack_BatchStore evaluates correct batch storage and retrieval of chunk data packs in the storage. 62 func TestChunkDataPacks_BatchStore(t *testing.T) { 63 WithChunkDataPacks(t, 100, func(t *testing.T, chunkDataPacks []*flow.ChunkDataPack, chunkDataPackStore *badgerstorage.ChunkDataPacks, db *badger.DB) { 64 batch := badgerstorage.NewBatch(db) 65 66 wg := sync.WaitGroup{} 67 wg.Add(len(chunkDataPacks)) 68 for _, chunkDataPack := range chunkDataPacks { 69 go func(cdp flow.ChunkDataPack) { 70 err := chunkDataPackStore.BatchStore(&cdp, batch) 71 require.NoError(t, err) 72 73 wg.Done() 74 }(*chunkDataPack) 75 } 76 77 unittest.RequireReturnsBefore(t, wg.Wait, 1*time.Second, "could not store chunk data packs on time") 78 79 err := batch.Flush() 80 require.NoError(t, err) 81 }) 82 } 83 84 // TestChunkDataPacks_MissingItem evaluates querying a missing item returns a storage.ErrNotFound error. 85 func TestChunkDataPacks_MissingItem(t *testing.T) { 86 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 87 transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) 88 collections := badgerstorage.NewCollections(db, transactions) 89 store := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) 90 91 // attempt to get an invalid 92 _, err := store.ByChunkID(unittest.IdentifierFixture()) 93 assert.True(t, errors.Is(err, storage.ErrNotFound)) 94 }) 95 } 96 97 // TestChunkDataPacks_StoreTwice evaluates that storing the same chunk data pack twice 98 // does not result in an error. 99 func TestChunkDataPacks_StoreTwice(t *testing.T) { 100 WithChunkDataPacks(t, 2, func(t *testing.T, chunkDataPacks []*flow.ChunkDataPack, chunkDataPackStore *badgerstorage.ChunkDataPacks, db *badger.DB) { 101 transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) 102 collections := badgerstorage.NewCollections(db, transactions) 103 store := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) 104 require.NoError(t, store.Store(chunkDataPacks)) 105 106 for _, c := range chunkDataPacks { 107 c2, err := store.ByChunkID(c.ChunkID) 108 require.NoError(t, err) 109 require.Equal(t, c, c2) 110 } 111 112 require.NoError(t, store.Store(chunkDataPacks)) 113 }) 114 } 115 116 // WithChunkDataPacks is a test helper that generates specified number of chunk data packs, store them using the storeFunc, and 117 // then evaluates whether they are successfully retrieved from storage. 118 func WithChunkDataPacks(t *testing.T, chunks int, storeFunc func(*testing.T, []*flow.ChunkDataPack, *badgerstorage.ChunkDataPacks, *badger.DB)) { 119 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 120 transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) 121 collections := badgerstorage.NewCollections(db, transactions) 122 // keep the cache size at 1 to make sure that entries are written and read from storage itself. 123 store := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) 124 125 chunkDataPacks := unittest.ChunkDataPacksFixture(chunks) 126 for _, chunkDataPack := range chunkDataPacks { 127 // stores collection in Collections storage (which ChunkDataPacks store uses internally) 128 err := collections.Store(chunkDataPack.Collection) 129 require.NoError(t, err) 130 } 131 132 // stores chunk data packs in the memory using provided store function. 133 storeFunc(t, chunkDataPacks, store, db) 134 135 // stored chunk data packs should be retrieved successfully. 136 for _, expected := range chunkDataPacks { 137 actual, err := store.ByChunkID(expected.ChunkID) 138 require.NoError(t, err) 139 140 assert.Equal(t, expected, actual) 141 } 142 }) 143 }