github.com/Finschia/finschia-sdk@v0.49.1/store/cachekv/store_test.go (about)

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