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