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 }