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  }