github.com/onflow/flow-go@v0.33.17/storage/badger/operation/common_test.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package operation 4 5 import ( 6 "bytes" 7 "fmt" 8 "reflect" 9 "testing" 10 11 "github.com/dgraph-io/badger/v2" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 "github.com/vmihailenco/msgpack/v4" 15 16 "github.com/onflow/flow-go/model/flow" 17 "github.com/onflow/flow-go/storage" 18 "github.com/onflow/flow-go/utils/unittest" 19 ) 20 21 type Entity struct { 22 ID uint64 23 } 24 25 type UnencodeableEntity Entity 26 27 var errCantEncode = fmt.Errorf("encoding not supported") 28 var errCantDecode = fmt.Errorf("decoding not supported") 29 30 func (a UnencodeableEntity) MarshalJSON() ([]byte, error) { 31 return nil, errCantEncode 32 } 33 34 func (a *UnencodeableEntity) UnmarshalJSON(b []byte) error { 35 return errCantDecode 36 } 37 38 func (a UnencodeableEntity) MarshalMsgpack() ([]byte, error) { 39 return nil, errCantEncode 40 } 41 42 func (a UnencodeableEntity) UnmarshalMsgpack(b []byte) error { 43 return errCantDecode 44 } 45 46 func TestInsertValid(t *testing.T) { 47 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 48 e := Entity{ID: 1337} 49 key := []byte{0x01, 0x02, 0x03} 50 val, _ := msgpack.Marshal(e) 51 52 err := db.Update(insert(key, e)) 53 require.NoError(t, err) 54 55 var act []byte 56 _ = db.View(func(tx *badger.Txn) error { 57 item, err := tx.Get(key) 58 require.NoError(t, err) 59 act, err = item.ValueCopy(nil) 60 require.NoError(t, err) 61 return nil 62 }) 63 64 assert.Equal(t, val, act) 65 }) 66 } 67 68 func TestInsertDuplicate(t *testing.T) { 69 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 70 e := Entity{ID: 1337} 71 key := []byte{0x01, 0x02, 0x03} 72 val, _ := msgpack.Marshal(e) 73 74 // persist first time 75 err := db.Update(insert(key, e)) 76 require.NoError(t, err) 77 78 e2 := Entity{ID: 1338} 79 80 // persist again 81 err = db.Update(insert(key, e2)) 82 require.Error(t, err) 83 require.ErrorIs(t, err, storage.ErrAlreadyExists) 84 85 // ensure old value did not update 86 var act []byte 87 _ = db.View(func(tx *badger.Txn) error { 88 item, err := tx.Get(key) 89 require.NoError(t, err) 90 act, err = item.ValueCopy(nil) 91 require.NoError(t, err) 92 return nil 93 }) 94 95 assert.Equal(t, val, act) 96 }) 97 } 98 99 func TestInsertEncodingError(t *testing.T) { 100 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 101 e := Entity{ID: 1337} 102 key := []byte{0x01, 0x02, 0x03} 103 104 err := db.Update(insert(key, UnencodeableEntity(e))) 105 require.Error(t, err, errCantEncode) 106 require.NotErrorIs(t, err, storage.ErrNotFound) 107 }) 108 } 109 110 func TestUpdateValid(t *testing.T) { 111 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 112 e := Entity{ID: 1337} 113 key := []byte{0x01, 0x02, 0x03} 114 val, _ := msgpack.Marshal(e) 115 116 _ = db.Update(func(tx *badger.Txn) error { 117 err := tx.Set(key, []byte{}) 118 require.NoError(t, err) 119 return nil 120 }) 121 122 err := db.Update(update(key, e)) 123 require.NoError(t, err) 124 125 var act []byte 126 _ = db.View(func(tx *badger.Txn) error { 127 item, err := tx.Get(key) 128 require.NoError(t, err) 129 act, err = item.ValueCopy(nil) 130 require.NoError(t, err) 131 return nil 132 }) 133 134 assert.Equal(t, val, act) 135 }) 136 } 137 138 func TestUpdateMissing(t *testing.T) { 139 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 140 e := Entity{ID: 1337} 141 key := []byte{0x01, 0x02, 0x03} 142 143 err := db.Update(update(key, e)) 144 require.ErrorIs(t, err, storage.ErrNotFound) 145 146 // ensure nothing was written 147 _ = db.View(func(tx *badger.Txn) error { 148 _, err := tx.Get(key) 149 require.Equal(t, badger.ErrKeyNotFound, err) 150 return nil 151 }) 152 }) 153 } 154 155 func TestUpdateEncodingError(t *testing.T) { 156 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 157 e := Entity{ID: 1337} 158 key := []byte{0x01, 0x02, 0x03} 159 val, _ := msgpack.Marshal(e) 160 161 _ = db.Update(func(tx *badger.Txn) error { 162 err := tx.Set(key, val) 163 require.NoError(t, err) 164 return nil 165 }) 166 167 err := db.Update(update(key, UnencodeableEntity(e))) 168 require.Error(t, err) 169 require.NotErrorIs(t, err, storage.ErrNotFound) 170 171 // ensure value did not change 172 var act []byte 173 _ = db.View(func(tx *badger.Txn) error { 174 item, err := tx.Get(key) 175 require.NoError(t, err) 176 act, err = item.ValueCopy(nil) 177 require.NoError(t, err) 178 return nil 179 }) 180 181 assert.Equal(t, val, act) 182 }) 183 } 184 185 func TestUpsertEntry(t *testing.T) { 186 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 187 e := Entity{ID: 1337} 188 key := []byte{0x01, 0x02, 0x03} 189 val, _ := msgpack.Marshal(e) 190 191 // first upsert an non-existed entry 192 err := db.Update(insert(key, e)) 193 require.NoError(t, err) 194 195 var act []byte 196 _ = db.View(func(tx *badger.Txn) error { 197 item, err := tx.Get(key) 198 require.NoError(t, err) 199 act, err = item.ValueCopy(nil) 200 require.NoError(t, err) 201 return nil 202 }) 203 204 assert.Equal(t, val, act) 205 206 // next upsert the value with the same key 207 newEntity := Entity{ID: 1338} 208 newVal, _ := msgpack.Marshal(newEntity) 209 err = db.Update(upsert(key, newEntity)) 210 require.NoError(t, err) 211 212 _ = db.View(func(tx *badger.Txn) error { 213 item, err := tx.Get(key) 214 require.NoError(t, err) 215 act, err = item.ValueCopy(nil) 216 require.NoError(t, err) 217 return nil 218 }) 219 220 assert.Equal(t, newVal, act) 221 }) 222 } 223 224 func TestRetrieveValid(t *testing.T) { 225 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 226 e := Entity{ID: 1337} 227 key := []byte{0x01, 0x02, 0x03} 228 val, _ := msgpack.Marshal(e) 229 230 _ = db.Update(func(tx *badger.Txn) error { 231 err := tx.Set(key, val) 232 require.NoError(t, err) 233 return nil 234 }) 235 236 var act Entity 237 err := db.View(retrieve(key, &act)) 238 require.NoError(t, err) 239 240 assert.Equal(t, e, act) 241 }) 242 } 243 244 func TestRetrieveMissing(t *testing.T) { 245 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 246 key := []byte{0x01, 0x02, 0x03} 247 248 var act Entity 249 err := db.View(retrieve(key, &act)) 250 require.ErrorIs(t, err, storage.ErrNotFound) 251 }) 252 } 253 254 func TestRetrieveUnencodeable(t *testing.T) { 255 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 256 e := Entity{ID: 1337} 257 key := []byte{0x01, 0x02, 0x03} 258 val, _ := msgpack.Marshal(e) 259 260 _ = db.Update(func(tx *badger.Txn) error { 261 err := tx.Set(key, val) 262 require.NoError(t, err) 263 return nil 264 }) 265 266 var act *UnencodeableEntity 267 err := db.View(retrieve(key, &act)) 268 require.Error(t, err) 269 require.NotErrorIs(t, err, storage.ErrNotFound) 270 }) 271 } 272 273 // TestExists verifies that `exists` returns correct results in different scenarios. 274 func TestExists(t *testing.T) { 275 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 276 t.Run("non-existent key", func(t *testing.T) { 277 key := unittest.RandomBytes(32) 278 var _exists bool 279 err := db.View(exists(key, &_exists)) 280 require.NoError(t, err) 281 assert.False(t, _exists) 282 }) 283 284 t.Run("existent key", func(t *testing.T) { 285 key := unittest.RandomBytes(32) 286 err := db.Update(insert(key, unittest.RandomBytes(256))) 287 require.NoError(t, err) 288 289 var _exists bool 290 err = db.View(exists(key, &_exists)) 291 require.NoError(t, err) 292 assert.True(t, _exists) 293 }) 294 295 t.Run("removed key", func(t *testing.T) { 296 key := unittest.RandomBytes(32) 297 // insert, then remove the key 298 err := db.Update(insert(key, unittest.RandomBytes(256))) 299 require.NoError(t, err) 300 err = db.Update(remove(key)) 301 require.NoError(t, err) 302 303 var _exists bool 304 err = db.View(exists(key, &_exists)) 305 require.NoError(t, err) 306 assert.False(t, _exists) 307 }) 308 }) 309 } 310 311 func TestLookup(t *testing.T) { 312 expected := []flow.Identifier{ 313 {0x01}, 314 {0x02}, 315 {0x03}, 316 {0x04}, 317 } 318 actual := []flow.Identifier{} 319 320 iterationFunc := lookup(&actual) 321 322 for _, e := range expected { 323 checkFunc, createFunc, handleFunc := iterationFunc() 324 assert.True(t, checkFunc([]byte{0x00})) 325 target := createFunc() 326 assert.IsType(t, &flow.Identifier{}, target) 327 328 // set the value to target. Need to use reflection here since target is not strongly typed 329 reflect.ValueOf(target).Elem().Set(reflect.ValueOf(e)) 330 331 assert.NoError(t, handleFunc()) 332 } 333 334 assert.Equal(t, expected, actual) 335 } 336 337 func TestIterate(t *testing.T) { 338 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 339 keys := [][]byte{{0x00}, {0x12}, {0xf0}, {0xff}} 340 vals := []bool{false, false, true, true} 341 expected := []bool{false, true} 342 343 _ = db.Update(func(tx *badger.Txn) error { 344 for i, key := range keys { 345 enc, err := msgpack.Marshal(vals[i]) 346 require.NoError(t, err) 347 err = tx.Set(key, enc) 348 require.NoError(t, err) 349 } 350 return nil 351 }) 352 353 actual := make([]bool, 0, len(keys)) 354 iterationFunc := func() (checkFunc, createFunc, handleFunc) { 355 check := func(key []byte) bool { 356 return !bytes.Equal(key, []byte{0x12}) 357 } 358 var val bool 359 create := func() interface{} { 360 return &val 361 } 362 handle := func() error { 363 actual = append(actual, val) 364 return nil 365 } 366 return check, create, handle 367 } 368 369 err := db.View(iterate(keys[0], keys[2], iterationFunc)) 370 require.Nil(t, err) 371 372 assert.Equal(t, expected, actual) 373 }) 374 } 375 376 func TestTraverse(t *testing.T) { 377 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 378 keys := [][]byte{{0x42, 0x00}, {0xff}, {0x42, 0x56}, {0x00}, {0x42, 0xff}} 379 vals := []bool{false, false, true, false, true} 380 expected := []bool{false, true} 381 382 _ = db.Update(func(tx *badger.Txn) error { 383 for i, key := range keys { 384 enc, err := msgpack.Marshal(vals[i]) 385 require.NoError(t, err) 386 err = tx.Set(key, enc) 387 require.NoError(t, err) 388 } 389 return nil 390 }) 391 392 actual := make([]bool, 0, len(keys)) 393 iterationFunc := func() (checkFunc, createFunc, handleFunc) { 394 check := func(key []byte) bool { 395 return !bytes.Equal(key, []byte{0x42, 0x56}) 396 } 397 var val bool 398 create := func() interface{} { 399 return &val 400 } 401 handle := func() error { 402 actual = append(actual, val) 403 return nil 404 } 405 return check, create, handle 406 } 407 408 err := db.View(traverse([]byte{0x42}, iterationFunc)) 409 require.Nil(t, err) 410 411 assert.Equal(t, expected, actual) 412 }) 413 } 414 415 func TestRemove(t *testing.T) { 416 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 417 e := Entity{ID: 1337} 418 key := []byte{0x01, 0x02, 0x03} 419 val, _ := msgpack.Marshal(e) 420 421 _ = db.Update(func(tx *badger.Txn) error { 422 err := tx.Set(key, val) 423 require.NoError(t, err) 424 return nil 425 }) 426 427 t.Run("should be able to remove", func(t *testing.T) { 428 _ = db.Update(func(txn *badger.Txn) error { 429 err := remove(key)(txn) 430 assert.NoError(t, err) 431 432 _, err = txn.Get(key) 433 assert.ErrorIs(t, err, badger.ErrKeyNotFound) 434 435 return nil 436 }) 437 }) 438 439 t.Run("should error when removing non-existing value", func(t *testing.T) { 440 nonexistantKey := append(key, 0x01) 441 _ = db.Update(func(txn *badger.Txn) error { 442 err := remove(nonexistantKey)(txn) 443 assert.ErrorIs(t, err, storage.ErrNotFound) 444 assert.Error(t, err) 445 return nil 446 }) 447 }) 448 }) 449 } 450 451 func TestRemoveByPrefix(t *testing.T) { 452 t.Run("should no-op when removing non-existing value", func(t *testing.T) { 453 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 454 e := Entity{ID: 1337} 455 key := []byte{0x01, 0x02, 0x03} 456 val, _ := msgpack.Marshal(e) 457 458 _ = db.Update(func(tx *badger.Txn) error { 459 err := tx.Set(key, val) 460 assert.NoError(t, err) 461 return nil 462 }) 463 464 nonexistantKey := append(key, 0x01) 465 err := db.Update(removeByPrefix(nonexistantKey)) 466 assert.NoError(t, err) 467 468 var act Entity 469 err = db.View(retrieve(key, &act)) 470 require.NoError(t, err) 471 472 assert.Equal(t, e, act) 473 }) 474 }) 475 476 t.Run("should be able to remove", func(t *testing.T) { 477 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 478 e := Entity{ID: 1337} 479 key := []byte{0x01, 0x02, 0x03} 480 val, _ := msgpack.Marshal(e) 481 482 _ = db.Update(func(tx *badger.Txn) error { 483 err := tx.Set(key, val) 484 assert.NoError(t, err) 485 return nil 486 }) 487 488 _ = db.Update(func(txn *badger.Txn) error { 489 prefix := []byte{0x01, 0x02} 490 err := removeByPrefix(prefix)(txn) 491 assert.NoError(t, err) 492 493 _, err = txn.Get(key) 494 assert.Error(t, err) 495 assert.IsType(t, badger.ErrKeyNotFound, err) 496 497 return nil 498 }) 499 }) 500 }) 501 502 t.Run("should be able to remove by key", func(t *testing.T) { 503 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 504 e := Entity{ID: 1337} 505 key := []byte{0x01, 0x02, 0x03} 506 val, _ := msgpack.Marshal(e) 507 508 _ = db.Update(func(tx *badger.Txn) error { 509 err := tx.Set(key, val) 510 assert.NoError(t, err) 511 return nil 512 }) 513 514 _ = db.Update(func(txn *badger.Txn) error { 515 err := removeByPrefix(key)(txn) 516 assert.NoError(t, err) 517 518 _, err = txn.Get(key) 519 assert.Error(t, err) 520 assert.IsType(t, badger.ErrKeyNotFound, err) 521 522 return nil 523 }) 524 }) 525 }) 526 } 527 528 func TestIterateBoundaries(t *testing.T) { 529 530 // create range of keys covering all boundaries around our start/end values 531 start := []byte{0x10} 532 end := []byte{0x20} 533 keys := [][]byte{ 534 // before start -> not included in range 535 {0x09, 0xff}, 536 // shares prefix with start -> included in range 537 {0x10, 0x00}, 538 {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 539 {0x10, 0xff}, 540 {0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 541 // prefix between start and end -> included in range 542 {0x11, 0x00}, 543 {0x19, 0xff}, 544 // shares prefix with end -> included in range 545 {0x20, 0x00}, 546 {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 547 {0x20, 0xff}, 548 {0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 549 // after end -> not included in range 550 {0x21, 0x00}, 551 } 552 553 // set the maximum current DB key range 554 for _, key := range keys { 555 if uint32(len(key)) > max { 556 max = uint32(len(key)) 557 } 558 } 559 560 // keys within the expected range 561 keysInRange := keys[1:11] 562 563 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 564 565 // insert the keys into the database 566 _ = db.Update(func(tx *badger.Txn) error { 567 for _, key := range keys { 568 err := tx.Set(key, []byte{0x00}) 569 if err != nil { 570 return err 571 } 572 } 573 return nil 574 }) 575 576 // define iteration function that simply appends all traversed keys 577 var found [][]byte 578 iteration := func() (checkFunc, createFunc, handleFunc) { 579 check := func(key []byte) bool { 580 found = append(found, key) 581 return false 582 } 583 create := func() interface{} { 584 return nil 585 } 586 handle := func() error { 587 return fmt.Errorf("shouldn't handle anything") 588 } 589 return check, create, handle 590 } 591 592 // iterate forward and check boundaries are included correctly 593 found = nil 594 err := db.View(iterate(start, end, iteration)) 595 for i, f := range found { 596 t.Logf("forward %d: %x", i, f) 597 } 598 require.NoError(t, err, "should iterate forward without error") 599 assert.ElementsMatch(t, keysInRange, found, "forward iteration should go over correct keys") 600 601 // iterate backward and check boundaries are included correctly 602 found = nil 603 err = db.View(iterate(end, start, iteration)) 604 for i, f := range found { 605 t.Logf("backward %d: %x", i, f) 606 } 607 require.NoError(t, err, "should iterate backward without error") 608 assert.ElementsMatch(t, keysInRange, found, "backward iteration should go over correct keys") 609 }) 610 } 611 612 func TestFindHighestAtOrBelow(t *testing.T) { 613 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 614 prefix := []byte("test_prefix") 615 616 type Entity struct { 617 Value uint64 618 } 619 620 entity1 := Entity{Value: 41} 621 entity2 := Entity{Value: 42} 622 entity3 := Entity{Value: 43} 623 624 err := db.Update(func(tx *badger.Txn) error { 625 key := append(prefix, b(uint64(15))...) 626 val, err := msgpack.Marshal(entity3) 627 if err != nil { 628 return err 629 } 630 err = tx.Set(key, val) 631 if err != nil { 632 return err 633 } 634 635 key = append(prefix, b(uint64(5))...) 636 val, err = msgpack.Marshal(entity1) 637 if err != nil { 638 return err 639 } 640 err = tx.Set(key, val) 641 if err != nil { 642 return err 643 } 644 645 key = append(prefix, b(uint64(10))...) 646 val, err = msgpack.Marshal(entity2) 647 if err != nil { 648 return err 649 } 650 err = tx.Set(key, val) 651 if err != nil { 652 return err 653 } 654 return nil 655 }) 656 require.NoError(t, err) 657 658 var entity Entity 659 660 t.Run("target height exists", func(t *testing.T) { 661 err = findHighestAtOrBelow( 662 prefix, 663 10, 664 &entity)(db.NewTransaction(false)) 665 require.NoError(t, err) 666 require.Equal(t, uint64(42), entity.Value) 667 }) 668 669 t.Run("target height above", func(t *testing.T) { 670 err = findHighestAtOrBelow( 671 prefix, 672 11, 673 &entity)(db.NewTransaction(false)) 674 require.NoError(t, err) 675 require.Equal(t, uint64(42), entity.Value) 676 }) 677 678 t.Run("target height above highest", func(t *testing.T) { 679 err = findHighestAtOrBelow( 680 prefix, 681 20, 682 &entity)(db.NewTransaction(false)) 683 require.NoError(t, err) 684 require.Equal(t, uint64(43), entity.Value) 685 }) 686 687 t.Run("target height below lowest", func(t *testing.T) { 688 err = findHighestAtOrBelow( 689 prefix, 690 4, 691 &entity)(db.NewTransaction(false)) 692 require.ErrorIs(t, err, storage.ErrNotFound) 693 }) 694 695 t.Run("empty prefix", func(t *testing.T) { 696 err = findHighestAtOrBelow( 697 []byte{}, 698 5, 699 &entity)(db.NewTransaction(false)) 700 require.Error(t, err) 701 require.Contains(t, err.Error(), "prefix must not be empty") 702 }) 703 }) 704 }