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