github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/store/cache/store_test.go (about)

     1  package cache_test
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	dbm "github.com/gnolang/gno/tm2/pkg/db"
    10  	"github.com/gnolang/gno/tm2/pkg/db/memdb"
    11  	"github.com/gnolang/gno/tm2/pkg/random"
    12  	"github.com/gnolang/gno/tm2/pkg/store/cache"
    13  	"github.com/gnolang/gno/tm2/pkg/store/dbadapter"
    14  	"github.com/gnolang/gno/tm2/pkg/store/types"
    15  )
    16  
    17  func newCacheStore() types.Store {
    18  	mem := dbadapter.Store{memdb.NewMemDB()}
    19  	return cache.New(mem)
    20  }
    21  
    22  func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) }
    23  func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) }
    24  
    25  func TestCacheStore(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	mem := dbadapter.Store{memdb.NewMemDB()}
    29  	st := cache.New(mem)
    30  
    31  	require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
    32  
    33  	// put something in mem and in cache
    34  	mem.Set(keyFmt(1), valFmt(1))
    35  	st.Set(keyFmt(1), valFmt(1))
    36  	require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
    37  
    38  	// update it in cache, shouldn't change mem
    39  	st.Set(keyFmt(1), valFmt(2))
    40  	require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
    41  	require.Equal(t, valFmt(1), mem.Get(keyFmt(1)))
    42  
    43  	// write it. should change mem
    44  	st.Write()
    45  	require.Equal(t, valFmt(2), mem.Get(keyFmt(1)))
    46  	require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
    47  
    48  	// more writes and checks
    49  	st.Write()
    50  	st.Write()
    51  	require.Equal(t, valFmt(2), mem.Get(keyFmt(1)))
    52  	require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
    53  
    54  	// make a new one, check it
    55  	st = cache.New(mem)
    56  	require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
    57  
    58  	// make a new one and delete - should not be removed from mem
    59  	st = cache.New(mem)
    60  	st.Delete(keyFmt(1))
    61  	require.Empty(t, st.Get(keyFmt(1)))
    62  	require.Equal(t, mem.Get(keyFmt(1)), valFmt(2))
    63  
    64  	// Write. should now be removed from both
    65  	st.Write()
    66  	require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
    67  	require.Empty(t, mem.Get(keyFmt(1)), "Expected `key1` to be empty")
    68  }
    69  
    70  func TestCacheStoreNoNilSet(t *testing.T) {
    71  	t.Parallel()
    72  
    73  	mem := dbadapter.Store{memdb.NewMemDB()}
    74  	st := cache.New(mem)
    75  	require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic")
    76  }
    77  
    78  func TestCacheStoreNested(t *testing.T) {
    79  	t.Parallel()
    80  
    81  	mem := dbadapter.Store{memdb.NewMemDB()}
    82  	st := cache.New(mem)
    83  
    84  	// set. check its there on st and not on mem.
    85  	st.Set(keyFmt(1), valFmt(1))
    86  	require.Empty(t, mem.Get(keyFmt(1)))
    87  	require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
    88  
    89  	// make a new from st and check
    90  	st2 := cache.New(st)
    91  	require.Equal(t, valFmt(1), st2.Get(keyFmt(1)))
    92  
    93  	// update the value on st2, check it only effects st2
    94  	st2.Set(keyFmt(1), valFmt(3))
    95  	require.Equal(t, []byte(nil), mem.Get(keyFmt(1)))
    96  	require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
    97  	require.Equal(t, valFmt(3), st2.Get(keyFmt(1)))
    98  
    99  	// st2 writes to its parent, st. doesn't effect mem
   100  	st2.Write()
   101  	require.Equal(t, []byte(nil), mem.Get(keyFmt(1)))
   102  	require.Equal(t, valFmt(3), st.Get(keyFmt(1)))
   103  
   104  	// updates mem
   105  	st.Write()
   106  	require.Equal(t, valFmt(3), mem.Get(keyFmt(1)))
   107  }
   108  
   109  func TestCacheKVIteratorBounds(t *testing.T) {
   110  	t.Parallel()
   111  
   112  	st := newCacheStore()
   113  
   114  	// set some items
   115  	nItems := 5
   116  	for i := 0; i < nItems; i++ {
   117  		st.Set(keyFmt(i), valFmt(i))
   118  	}
   119  
   120  	// iterate over all of them
   121  	itr := st.Iterator(nil, nil)
   122  	i := 0
   123  	for ; itr.Valid(); itr.Next() {
   124  		k, v := itr.Key(), itr.Value()
   125  		require.Equal(t, keyFmt(i), k)
   126  		require.Equal(t, valFmt(i), v)
   127  		i++
   128  	}
   129  	require.Equal(t, nItems, i)
   130  
   131  	// iterate over none
   132  	itr = st.Iterator(bz("money"), nil)
   133  	i = 0
   134  	for ; itr.Valid(); itr.Next() {
   135  		i++
   136  	}
   137  	require.Equal(t, 0, i)
   138  
   139  	// iterate over lower
   140  	itr = st.Iterator(keyFmt(0), keyFmt(3))
   141  	i = 0
   142  	for ; itr.Valid(); itr.Next() {
   143  		k, v := itr.Key(), itr.Value()
   144  		require.Equal(t, keyFmt(i), k)
   145  		require.Equal(t, valFmt(i), v)
   146  		i++
   147  	}
   148  	require.Equal(t, 3, i)
   149  
   150  	// iterate over upper
   151  	itr = st.Iterator(keyFmt(2), keyFmt(4))
   152  	i = 2
   153  	for ; itr.Valid(); itr.Next() {
   154  		k, v := itr.Key(), itr.Value()
   155  		require.Equal(t, keyFmt(i), k)
   156  		require.Equal(t, valFmt(i), v)
   157  		i++
   158  	}
   159  	require.Equal(t, 4, i)
   160  }
   161  
   162  func TestCacheKVReverseIteratorBounds(t *testing.T) {
   163  	t.Parallel()
   164  
   165  	st := newCacheStore()
   166  
   167  	// set some items
   168  	nItems := 5
   169  	for i := 0; i < nItems; i++ {
   170  		st.Set(keyFmt(i), valFmt(i))
   171  	}
   172  
   173  	// iterate over all of them in reverse
   174  	i := nItems - 1
   175  	for itr := st.ReverseIterator(nil, nil); itr.Valid(); itr.Next() {
   176  		require.Equal(t, keyFmt(i), itr.Key())
   177  		require.Equal(t, valFmt(i), itr.Value())
   178  		i--
   179  	}
   180  	require.Equal(t, -1, i)
   181  
   182  	// iterate over none
   183  	i = 0
   184  	for itr := st.ReverseIterator(bz("money"), nil); itr.Valid(); itr.Next() {
   185  		i++
   186  	}
   187  	require.Equal(t, 0, i)
   188  
   189  	// iterate over lower
   190  	i = 2
   191  	for itr := st.ReverseIterator(keyFmt(0), keyFmt(3)); itr.Valid(); itr.Next() {
   192  		require.Equal(t, keyFmt(i), itr.Key())
   193  		require.Equal(t, valFmt(i), itr.Value())
   194  		i--
   195  	}
   196  	require.Equal(t, -1, i)
   197  
   198  	// iterate over upper
   199  	i = 3
   200  	for itr := st.ReverseIterator(keyFmt(2), keyFmt(4)); itr.Valid(); itr.Next() {
   201  		require.Equal(t, keyFmt(i), itr.Key())
   202  		require.Equal(t, valFmt(i), itr.Value())
   203  		i--
   204  	}
   205  	require.Equal(t, 1, i)
   206  }
   207  
   208  func TestCacheKVMergeIteratorBasics(t *testing.T) {
   209  	t.Parallel()
   210  
   211  	st := newCacheStore()
   212  
   213  	// set and delete an item in the cache, iterator should be empty
   214  	k, v := keyFmt(0), valFmt(0)
   215  	st.Set(k, v)
   216  	st.Delete(k)
   217  	assertIterateDomain(t, st, 0)
   218  
   219  	// now set it and assert its there
   220  	st.Set(k, v)
   221  	assertIterateDomain(t, st, 1)
   222  
   223  	// write it and assert its there
   224  	st.Write()
   225  	assertIterateDomain(t, st, 1)
   226  
   227  	// remove it in cache and assert its not
   228  	st.Delete(k)
   229  	assertIterateDomain(t, st, 0)
   230  
   231  	// write the delete and assert its not there
   232  	st.Write()
   233  	assertIterateDomain(t, st, 0)
   234  
   235  	// add two keys and assert they're there
   236  	k1, v1 := keyFmt(1), valFmt(1)
   237  	st.Set(k, v)
   238  	st.Set(k1, v1)
   239  	assertIterateDomain(t, st, 2)
   240  
   241  	// write it and assert they're there
   242  	st.Write()
   243  	assertIterateDomain(t, st, 2)
   244  
   245  	// remove one in cache and assert its not
   246  	st.Delete(k1)
   247  	assertIterateDomain(t, st, 1)
   248  
   249  	// write the delete and assert its not there
   250  	st.Write()
   251  	assertIterateDomain(t, st, 1)
   252  
   253  	// delete the other key in cache and asserts its empty
   254  	st.Delete(k)
   255  	assertIterateDomain(t, st, 0)
   256  }
   257  
   258  func TestCacheKVMergeIteratorDeleteLast(t *testing.T) {
   259  	t.Parallel()
   260  
   261  	st := newCacheStore()
   262  
   263  	// set some items and write them
   264  	nItems := 5
   265  	for i := 0; i < nItems; i++ {
   266  		st.Set(keyFmt(i), valFmt(i))
   267  	}
   268  	st.Write()
   269  
   270  	// set some more items and leave dirty
   271  	for i := nItems; i < nItems*2; i++ {
   272  		st.Set(keyFmt(i), valFmt(i))
   273  	}
   274  
   275  	// iterate over all of them
   276  	assertIterateDomain(t, st, nItems*2)
   277  
   278  	// delete them all
   279  	for i := 0; i < nItems*2; i++ {
   280  		last := nItems*2 - 1 - i
   281  		st.Delete(keyFmt(last))
   282  		assertIterateDomain(t, st, last)
   283  	}
   284  }
   285  
   286  func TestCacheKVMergeIteratorDeletes(t *testing.T) {
   287  	t.Parallel()
   288  
   289  	st := newCacheStore()
   290  	truth := memdb.NewMemDB()
   291  
   292  	// set some items and write them
   293  	nItems := 10
   294  	for i := 0; i < nItems; i++ {
   295  		doOp(st, truth, opSet, i)
   296  	}
   297  	st.Write()
   298  
   299  	// delete every other item, starting from 0
   300  	for i := 0; i < nItems; i += 2 {
   301  		doOp(st, truth, opDel, i)
   302  		assertIterateDomainCompare(t, st, truth)
   303  	}
   304  
   305  	// reset
   306  	st = newCacheStore()
   307  	truth = memdb.NewMemDB()
   308  
   309  	// set some items and write them
   310  	for i := 0; i < nItems; i++ {
   311  		doOp(st, truth, opSet, i)
   312  	}
   313  	st.Write()
   314  
   315  	// delete every other item, starting from 1
   316  	for i := 1; i < nItems; i += 2 {
   317  		doOp(st, truth, opDel, i)
   318  		assertIterateDomainCompare(t, st, truth)
   319  	}
   320  }
   321  
   322  func TestCacheKVMergeIteratorChunks(t *testing.T) {
   323  	t.Parallel()
   324  
   325  	st := newCacheStore()
   326  
   327  	// Use the truth to check values on the merge iterator
   328  	truth := memdb.NewMemDB()
   329  
   330  	// sets to the parent
   331  	setRange(st, truth, 0, 20)
   332  	setRange(st, truth, 40, 60)
   333  	st.Write()
   334  
   335  	// sets to the cache
   336  	setRange(st, truth, 20, 40)
   337  	setRange(st, truth, 60, 80)
   338  	assertIterateDomainCheck(t, st, truth, []keyRange{{0, 80}})
   339  
   340  	// remove some parents and some cache
   341  	deleteRange(st, truth, 15, 25)
   342  	assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 80}})
   343  
   344  	// remove some parents and some cache
   345  	deleteRange(st, truth, 35, 45)
   346  	assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {45, 80}})
   347  
   348  	// write, add more to the cache, and delete some cache
   349  	st.Write()
   350  	setRange(st, truth, 38, 42)
   351  	deleteRange(st, truth, 40, 43)
   352  	assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {38, 40}, {45, 80}})
   353  }
   354  
   355  func TestCacheKVMergeIteratorRandom(t *testing.T) {
   356  	t.Parallel()
   357  
   358  	st := newCacheStore()
   359  	truth := memdb.NewMemDB()
   360  
   361  	start, end := 25, 975
   362  	max := 1000
   363  	setRange(st, truth, start, end)
   364  
   365  	// do an op, test the iterator
   366  	for i := 0; i < 2000; i++ {
   367  		doRandomOp(st, truth, max)
   368  		assertIterateDomainCompare(t, st, truth)
   369  	}
   370  }
   371  
   372  // -------------------------------------------------------------------------------------------
   373  // do some random ops
   374  
   375  const (
   376  	opSet      = 0
   377  	opSetRange = 1
   378  	opDel      = 2
   379  	opDelRange = 3
   380  	opWrite    = 4
   381  
   382  	totalOps = 5 // number of possible operations
   383  )
   384  
   385  func randInt(n int) int {
   386  	return random.RandInt() % n
   387  }
   388  
   389  // useful for replaying a error case if we find one
   390  func doOp(st types.Store, truth dbm.DB, op int, args ...int) {
   391  	switch op {
   392  	case opSet:
   393  		k := args[0]
   394  		st.Set(keyFmt(k), valFmt(k))
   395  		truth.Set(keyFmt(k), valFmt(k))
   396  	case opSetRange:
   397  		start := args[0]
   398  		end := args[1]
   399  		setRange(st, truth, start, end)
   400  	case opDel:
   401  		k := args[0]
   402  		st.Delete(keyFmt(k))
   403  		truth.Delete(keyFmt(k))
   404  	case opDelRange:
   405  		start := args[0]
   406  		end := args[1]
   407  		deleteRange(st, truth, start, end)
   408  	case opWrite:
   409  		st.Write()
   410  	}
   411  }
   412  
   413  func doRandomOp(st types.Store, truth dbm.DB, maxKey int) {
   414  	r := randInt(totalOps)
   415  	switch r {
   416  	case opSet:
   417  		k := randInt(maxKey)
   418  		st.Set(keyFmt(k), valFmt(k))
   419  		truth.Set(keyFmt(k), valFmt(k))
   420  	case opSetRange:
   421  		start := randInt(maxKey - 2)
   422  		end := randInt(maxKey-start) + start
   423  		setRange(st, truth, start, end)
   424  	case opDel:
   425  		k := randInt(maxKey)
   426  		st.Delete(keyFmt(k))
   427  		truth.Delete(keyFmt(k))
   428  	case opDelRange:
   429  		start := randInt(maxKey - 2)
   430  		end := randInt(maxKey-start) + start
   431  		deleteRange(st, truth, start, end)
   432  	case opWrite:
   433  		st.Write()
   434  	}
   435  }
   436  
   437  // -------------------------------------------------------------------------------------------
   438  
   439  // iterate over whole domain
   440  func assertIterateDomain(t *testing.T, st types.Store, expectedN int) {
   441  	t.Helper()
   442  
   443  	itr := st.Iterator(nil, nil)
   444  	i := 0
   445  	for ; itr.Valid(); itr.Next() {
   446  		k, v := itr.Key(), itr.Value()
   447  		require.Equal(t, keyFmt(i), k)
   448  		require.Equal(t, valFmt(i), v)
   449  		i++
   450  	}
   451  	require.Equal(t, expectedN, i)
   452  }
   453  
   454  func assertIterateDomainCheck(t *testing.T, st types.Store, mem dbm.DB, r []keyRange) {
   455  	t.Helper()
   456  
   457  	// iterate over each and check they match the other
   458  	itr := st.Iterator(nil, nil)
   459  	itr2 := mem.Iterator(nil, nil) // ground truth
   460  
   461  	krc := newKeyRangeCounter(r)
   462  	i := 0
   463  
   464  	for ; krc.valid(); krc.next() {
   465  		require.True(t, itr.Valid())
   466  		require.True(t, itr2.Valid())
   467  
   468  		// check the key/val matches the ground truth
   469  		k, v := itr.Key(), itr.Value()
   470  		k2, v2 := itr2.Key(), itr2.Value()
   471  		require.Equal(t, k, k2)
   472  		require.Equal(t, v, v2)
   473  
   474  		// check they match the counter
   475  		require.Equal(t, k, keyFmt(krc.key()))
   476  
   477  		itr.Next()
   478  		itr2.Next()
   479  		i++
   480  	}
   481  
   482  	require.False(t, itr.Valid())
   483  	require.False(t, itr2.Valid())
   484  }
   485  
   486  func assertIterateDomainCompare(t *testing.T, st types.Store, mem dbm.DB) {
   487  	t.Helper()
   488  
   489  	// iterate over each and check they match the other
   490  	itr := st.Iterator(nil, nil)
   491  	itr2 := mem.Iterator(nil, nil) // ground truth
   492  	checkIterators(t, itr, itr2)
   493  	checkIterators(t, itr2, itr)
   494  }
   495  
   496  func checkIterators(t *testing.T, itr, itr2 types.Iterator) {
   497  	t.Helper()
   498  
   499  	for ; itr.Valid(); itr.Next() {
   500  		require.True(t, itr2.Valid())
   501  		k, v := itr.Key(), itr.Value()
   502  		k2, v2 := itr2.Key(), itr2.Value()
   503  		require.Equal(t, k, k2)
   504  		require.Equal(t, v, v2)
   505  		itr2.Next()
   506  	}
   507  	require.False(t, itr.Valid())
   508  	require.False(t, itr2.Valid())
   509  }
   510  
   511  // --------------------------------------------------------
   512  
   513  func setRange(st types.Store, mem dbm.DB, start, end int) {
   514  	for i := start; i < end; i++ {
   515  		st.Set(keyFmt(i), valFmt(i))
   516  		mem.Set(keyFmt(i), valFmt(i))
   517  	}
   518  }
   519  
   520  func deleteRange(st types.Store, mem dbm.DB, start, end int) {
   521  	for i := start; i < end; i++ {
   522  		st.Delete(keyFmt(i))
   523  		mem.Delete(keyFmt(i))
   524  	}
   525  }
   526  
   527  // --------------------------------------------------------
   528  
   529  type keyRange struct {
   530  	start int
   531  	end   int
   532  }
   533  
   534  func (kr keyRange) len() int {
   535  	return kr.end - kr.start
   536  }
   537  
   538  func newKeyRangeCounter(kr []keyRange) *keyRangeCounter {
   539  	return &keyRangeCounter{keyRanges: kr}
   540  }
   541  
   542  // we can iterate over this and make sure our real iterators have all the right keys
   543  type keyRangeCounter struct {
   544  	rangeIdx  int
   545  	idx       int
   546  	keyRanges []keyRange
   547  }
   548  
   549  func (krc *keyRangeCounter) valid() bool {
   550  	maxRangeIdx := len(krc.keyRanges) - 1
   551  	maxRange := krc.keyRanges[maxRangeIdx]
   552  
   553  	// if we're not in the max range, we're valid
   554  	if krc.rangeIdx <= maxRangeIdx &&
   555  		krc.idx < maxRange.len() {
   556  		return true
   557  	}
   558  
   559  	return false
   560  }
   561  
   562  func (krc *keyRangeCounter) next() {
   563  	thisKeyRange := krc.keyRanges[krc.rangeIdx]
   564  	if krc.idx == thisKeyRange.len()-1 {
   565  		krc.rangeIdx++
   566  		krc.idx = 0
   567  	} else {
   568  		krc.idx++
   569  	}
   570  }
   571  
   572  func (krc *keyRangeCounter) key() int {
   573  	thisKeyRange := krc.keyRanges[krc.rangeIdx]
   574  	return thisKeyRange.start + krc.idx
   575  }
   576  
   577  // --------------------------------------------------------
   578  
   579  func bz(s string) []byte { return []byte(s) }
   580  
   581  func BenchmarkCacheStoreGetNoKeyFound(b *testing.B) {
   582  	st := newCacheStore()
   583  	b.ResetTimer()
   584  	// assumes b.N < 2**24
   585  	for i := 0; i < b.N; i++ {
   586  		st.Get([]byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)})
   587  	}
   588  }
   589  
   590  func BenchmarkCacheStoreGetKeyFound(b *testing.B) {
   591  	if testing.Short() {
   592  		b.Skip("skipping testing in short mode")
   593  	}
   594  
   595  	st := newCacheStore()
   596  	for i := 0; i < b.N; i++ {
   597  		arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
   598  		st.Set(arr, arr)
   599  	}
   600  	b.ResetTimer()
   601  	// assumes b.N < 2**24
   602  	for i := 0; i < b.N; i++ {
   603  		st.Get([]byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)})
   604  	}
   605  }