github.com/cilium/statedb@v0.3.2/part/part_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package part 5 6 import ( 7 "bytes" 8 "encoding/binary" 9 "fmt" 10 "math/rand" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 const numObjectsToInsert = 1000 19 20 // Tests that the right channels are closed during insertion. 21 func Test_insertion_and_watches(t *testing.T) { 22 assertOpen := func(t testing.TB, c <-chan struct{}) { 23 t.Helper() 24 assert.NotNil(t, c) 25 select { 26 case <-c: 27 t.Error("closed, but should be open") 28 default: 29 } 30 } 31 32 assertClosed := func(t testing.TB, c <-chan struct{}) { 33 t.Helper() 34 assert.NotNil(t, c) 35 select { 36 case <-c: 37 default: 38 t.Error("open, but should be closed") 39 } 40 } 41 42 // Replacement 43 { 44 tree := New[int]() 45 46 txn := tree.Txn() 47 txn.Insert([]byte("abc"), 1) 48 txn.Insert([]byte("ab"), 2) 49 txn.Insert([]byte("abd"), 3) 50 tree = txn.Commit() 51 52 _, w, f := tree.Get([]byte("ab")) 53 assert.True(t, f) 54 assertOpen(t, w) 55 _, w2 := tree.Prefix([]byte("a")) 56 assertOpen(t, w2) 57 _, w3, f2 := tree.Get([]byte("abc")) 58 assert.True(t, f2) 59 assertOpen(t, w3) 60 _, w4 := tree.Prefix([]byte("abc")) 61 assertOpen(t, w4) 62 63 _, _, tree = tree.Insert([]byte("ab"), 42) 64 assertClosed(t, w) 65 assertClosed(t, w2) 66 67 assertOpen(t, w3) 68 69 // Prefix watch is closed as it's on the "ab" node 70 // since that's the closest non-leaf node to "abc" leaf. 71 assertClosed(t, w4) 72 } 73 74 // Root to leaf and back. 75 // N4() -- Insert(a, 1) -> N4() -- Insert(b, 2) -> N4() 76 // / / \ 77 // L(a, 1) L(a, 1) L(b, 2) 78 // The Get(a) channel closes, but not Prefix(a) since the prefix search 79 // uses the root channel. 80 { 81 tree := New[int]() 82 83 _, _, tree = tree.Insert([]byte("a"), 1) 84 85 _, w, f := tree.Get([]byte("a")) 86 assert.True(t, f) 87 assertOpen(t, w) 88 _, w2 := tree.Prefix([]byte("a")) 89 assertOpen(t, w2) 90 91 _, _, tree = tree.Insert([]byte("b"), 2) 92 assertOpen(t, w) 93 assertClosed(t, w2) 94 } 95 96 // "Lateral movement" - L(a, 1) should become the leaf of the root N4 97 { 98 tree := New[int]() 99 100 _, _, tree = tree.Insert([]byte("a"), 1) 101 102 _, w, f := tree.Get([]byte("a")) 103 assert.True(t, f) 104 assertOpen(t, w) 105 _, w2 := tree.Prefix([]byte("a")) 106 assertOpen(t, w2) 107 108 txn := tree.Txn() 109 txn.Insert([]byte("aa"), 2) 110 txn.Insert([]byte("ab"), 3) 111 assertOpen(t, w) // shouldn't close until commit 112 assertOpen(t, w2) 113 tree = txn.Commit() 114 115 assertOpen(t, w) 116 assertClosed(t, w2) 117 } 118 119 // Second variant of "lateral movement" of leaf node. 120 // N4(a) - L(a,1) N4(a) - L(a,1) 121 // / \ -- Insert(abc) --> / \ 122 // L(aa,2) L(ab,3) L(aa,2) N4(b) - L(ab, 3) 123 // / 124 // L(abc, 4) 125 { 126 tree := New[int]() 127 128 txn := tree.Txn() 129 txn.Insert([]byte("a"), 1) 130 txn.Insert([]byte("aa"), 2) 131 txn.Insert([]byte("ab"), 3) 132 tree = txn.Commit() 133 134 _, w, f := tree.Get([]byte("ab")) 135 assert.True(t, f) 136 assertOpen(t, w) 137 138 _, w2 := tree.Prefix([]byte("ab")) 139 assertOpen(t, w2) 140 141 // This should move the L(ab) laterally and insert a N4(ab) with a L(abc, 4) 142 // child 143 _, _, tree = tree.Insert([]byte("abc"), 4) 144 // The precise "ab" chan should be open. 145 assertOpen(t, w) 146 // The "ab" prefix chan should be closed. 147 assertClosed(t, w2) 148 } 149 150 } 151 152 func Test_commonPrefix(t *testing.T) { 153 check := func(a, b, common string) { 154 actual := string(commonPrefix([]byte(a), []byte(b))) 155 if actual != common { 156 t.Fatalf("expected commonPrefix(%q, %q) to equal %q, but got %q", 157 a, b, common, actual) 158 } 159 } 160 161 check("", "", "") 162 check("", "a", "") 163 check("a", "", "") 164 check("a", "a", "a") 165 check("a", "b", "") 166 check("abc", "d", "") 167 check("d", "abc", "") 168 check("ab", "abc", "ab") 169 check("abc", "ab", "ab") 170 } 171 172 func Test_search(t *testing.T) { 173 tree := New[[]byte](RootOnlyWatch) 174 _, _, tree = tree.Insert([]byte("a"), []byte("a")) 175 _, _, tree = tree.Insert([]byte("ba"), []byte("ba")) 176 _, _, tree = tree.Insert([]byte("bb"), []byte("bb")) 177 _, _, tree = tree.Insert([]byte("c"), []byte("c")) 178 _, _, tree = tree.Insert([]byte("ca"), []byte("ca")) 179 180 v, _, ok := tree.Get([]byte("nope")) 181 if ok { 182 t.Fatalf("found unexpected value: %v", v) 183 } 184 185 for _, key := range []string{"a", "ba", "bb"} { 186 v, _, ok = tree.Get([]byte(key)) 187 if !ok || string(v) != key { 188 t.Fatalf("%q not found (%v) or mismatch %q", key, ok, v) 189 } 190 } 191 } 192 193 func uint64Key(n uint64) []byte { 194 return binary.BigEndian.AppendUint64(nil, n) 195 } 196 197 func hexKey(n uint64) []byte { 198 return []byte(fmt.Sprintf("%x", n)) 199 } 200 201 func uint32Key(n uint32) []byte { 202 return binary.BigEndian.AppendUint32(nil, n) 203 } 204 205 func Test_simple_delete(t *testing.T) { 206 tree := New[uint64]() 207 txn := tree.Txn() 208 209 _, hadOld := txn.Insert(uint64Key(1), 1) 210 require.False(t, hadOld) 211 212 _, hadOld = txn.Insert(uint64Key(2), 2) 213 require.False(t, hadOld) 214 215 _, hadOld = txn.Delete(uint64Key(1)) 216 require.True(t, hadOld) 217 218 _, _, ok := txn.Get(uint64Key(1)) 219 require.False(t, ok) 220 } 221 222 func Test_delete(t *testing.T) { 223 tree := New[uint64]() 224 225 // Do multiple rounds with the same tree. 226 for round := 0; round < 100; round++ { 227 // Use a random amount of keys in random order to exercise different 228 // tree structures each time. 229 numKeys := 10 + rand.Intn(1000) 230 t.Logf("numKeys=%d", numKeys) 231 232 keys := []uint64{} 233 for i := uint64(1); i < uint64(numKeys); i++ { 234 keys = append(keys, i) 235 } 236 hadOld := false 237 238 // Insert the keys in random order. 239 rand.Shuffle(len(keys), func(i, j int) { 240 keys[i], keys[j] = keys[j], keys[i] 241 }) 242 243 txn := tree.Txn() 244 for _, i := range keys { 245 _, hadOld = txn.Insert(uint64Key(i), i) 246 assert.False(t, hadOld) 247 v, _, ok := txn.Get(uint64Key(i)) 248 assert.True(t, ok) 249 assert.EqualValues(t, v, i) 250 } 251 tree = txn.Commit() 252 assert.Equal(t, len(keys), tree.Len()) 253 254 // Delete the keys in random order. 255 rand.Shuffle(len(keys), func(i, j int) { 256 keys[i], keys[j] = keys[j], keys[i] 257 }) 258 259 txn = tree.Txn() 260 for _, i := range keys { 261 v, _, ok := txn.Get(uint64Key(i)) 262 assert.True(t, ok) 263 assert.EqualValues(t, v, i) 264 v, hadOld = txn.Delete(uint64Key(i)) 265 assert.True(t, hadOld) 266 assert.EqualValues(t, v, i) 267 _, _, ok = txn.Get(uint64Key(i)) 268 assert.False(t, ok) 269 } 270 tree = txn.Commit() 271 272 assert.Equal(t, 0, tree.Len()) 273 for _, i := range keys { 274 _, _, ok := tree.Get(uint64Key(i)) 275 assert.False(t, ok) 276 } 277 278 // And finally insert the keys back one more time 279 // in random order. 280 rand.Shuffle(len(keys), func(i, j int) { 281 keys[i], keys[j] = keys[j], keys[i] 282 }) 283 284 watches := map[uint64]<-chan struct{}{} 285 286 txn = tree.Txn() 287 for _, i := range keys { 288 _, hadOld = txn.Insert(uint64Key(i), i) 289 assert.False(t, hadOld) 290 v, watch, ok := txn.Get(uint64Key(i)) 291 watches[i] = watch 292 assert.True(t, ok) 293 assert.EqualValues(t, v, i) 294 } 295 tree = txn.Commit() 296 assert.Equal(t, len(keys), tree.Len()) 297 298 // Do few rounds of lookups and deletions. 299 for step := 0; step < 2; step++ { 300 // Lookup with a Txn 301 txn = tree.Txn() 302 for _, i := range keys { 303 v, _, ok := txn.Get(uint64Key(i)) 304 assert.True(t, ok) 305 assert.EqualValues(t, v, i) 306 } 307 txn = nil 308 309 // Test that full iteration is ordered 310 iter := tree.Iterator() 311 prev := uint64(0) 312 num := 0 313 for { 314 _, v, ok := iter.Next() 315 if !ok { 316 break 317 } 318 num++ 319 require.Greater(t, v, prev) 320 prev = v 321 } 322 assert.Equal(t, num, len(keys)) 323 324 // Test that lowerbound iteration is ordered and correct 325 idx := len(keys) / 2 326 prev = keys[idx] 327 num = 0 328 start := prev + 1 329 iter = tree.LowerBound(uint64Key(start)) 330 obs := []uint64{} 331 for { 332 _, v, ok := iter.Next() 333 if !ok { 334 break 335 } 336 num++ 337 obs = append(obs, v) 338 require.Greater(t, v, prev) 339 prev = v 340 } 341 exp := 0 342 for _, k := range keys { 343 if k >= start { 344 exp++ 345 } 346 } 347 if !assert.Equal(t, exp, num) { 348 t.Logf("LowerBound from %d failed", start) 349 t.Logf("observed: %v", obs) 350 tree.PrintTree() 351 t.Fatal() 352 } 353 354 // Test that prefix iteration is ordered and correct 355 prev = 0 356 iter, _ = tree.Prefix([]byte{}) 357 for { 358 _, v, ok := iter.Next() 359 if !ok { 360 break 361 } 362 require.Greater(t, v, prev) 363 prev = v 364 } 365 366 // Remove half the keys 367 for _, k := range keys[:len(keys)/2] { 368 _, _, tree = tree.Delete(uint64Key(k)) 369 } 370 keys = keys[len(keys)/2:] 371 } 372 373 // Remove everything remaining with iteration 374 txn = tree.Txn() 375 iter := txn.Iterator() 376 for k, _, ok := iter.Next(); ok; k, _, ok = iter.Next() { 377 _, hadOld = txn.Delete(k) 378 assert.True(t, hadOld) 379 380 _, _, ok := txn.Get(k) 381 assert.False(t, ok) 382 } 383 384 // Check that we can iterate with the transaction and 385 // everything is gone. 386 iter = txn.Iterator() 387 _, _, ok := iter.Next() 388 assert.False(t, ok) 389 390 tree = txn.Commit() 391 392 // Check that all the watch channels closed 393 for _, watch := range watches { 394 <-watch 395 } 396 397 // Check that everything is gone after commit. 398 for _, i := range keys { 399 _, _, ok := tree.Get(uint64Key(i)) 400 assert.False(t, ok) 401 } 402 403 assert.Equal(t, 0, tree.Len()) 404 } 405 } 406 407 func Test_watch(t *testing.T) { 408 tree := New[[]byte]() 409 410 // Insert 'a', get it and check watch channel is not closed. 411 _, _, tree = tree.Insert([]byte("a"), []byte("a")) 412 413 _, watchA, ok := tree.Get([]byte("a")) 414 if !ok { 415 t.Fatal("expected to find 'a'") 416 } 417 select { 418 case <-watchA: 419 t.Fatal("did not expect watch to be closed") 420 default: 421 } 422 423 // Get 'b' that should not exist and the watch channel should 424 // not be closed. 425 _, watchB, ok := tree.Get([]byte("b")) 426 assert.False(t, ok, "Get(b)") 427 428 select { 429 case <-watchB: 430 t.Fatal("did not expect watch to be closed") 431 default: 432 } 433 434 // Modify 'a'. Now the watch channel should close. 435 _, _, tree = tree.Insert([]byte("a"), []byte("aa")) 436 437 select { 438 case <-watchA: 439 case <-time.After(10 * time.Second): 440 t.Fatal("expected watch channel to close") 441 } 442 443 v, _, ok := tree.Get([]byte("a")) 444 if !ok { 445 t.Fatal("expected to find 'a'") 446 } 447 if string(v) != "aa" { 448 t.Fatalf("expected value 'aa', got '%s'", v) 449 } 450 451 // Insert 'b'. Now the watch channel should close. 452 _, _, tree = tree.Insert([]byte("b"), []byte("b")) 453 select { 454 case <-watchB: 455 case <-time.After(10 * time.Second): 456 t.Fatal("expected watch channel to close") 457 } 458 } 459 460 func Test_insert(t *testing.T) { 461 tree := New[int]() 462 for i := 0; i < 10000; i++ { 463 key := binary.NativeEndian.AppendUint32(nil, uint32(i)) 464 _, _, tree = tree.Insert(key, i) 465 } 466 for i := 0; i < 10000; i++ { 467 key := binary.NativeEndian.AppendUint32(nil, uint32(i)) 468 _, _, ok := tree.Get(key) 469 if !ok { 470 t.Fatalf("%d not found", i) 471 } 472 } 473 } 474 475 func Test_modify(t *testing.T) { 476 tree := New[int]() 477 key := []byte{1} 478 479 // Modify without the value existing inserts it. 480 _, _, tree = tree.Modify(key, func(x int) int { return 1 }) 481 482 v, _, ok := tree.Get(key) 483 require.True(t, ok) 484 require.Equal(t, 1, v) 485 486 txn := tree.Txn() 487 for i := range 10000 { 488 old, hadOld := txn.Modify(key, func(x int) int { return x + 1 }) 489 require.True(t, hadOld) 490 require.Equal(t, i+1, old) 491 } 492 tree = txn.Commit() 493 494 v, _, ok = tree.Get(key) 495 require.True(t, ok) 496 require.Equal(t, 10001, v) 497 } 498 499 func Test_replaceRoot(t *testing.T) { 500 tree := New[int]() 501 keyA := []byte{'a'} 502 keyB := []byte{'a', 'b'} 503 _, _, tree = tree.Insert(keyA, 1) 504 _, _, tree = tree.Insert(keyB, 3) 505 _, _, tree = tree.Delete(keyA) 506 _, _, tree = tree.Insert(keyA, 2) 507 val, _, ok := tree.Get(keyA) 508 if !ok || val != 2 { 509 t.Fatalf("%v not found", keyA) 510 } 511 val, _, ok = tree.Get(keyB) 512 if !ok || val != 3 { 513 t.Fatalf("%v not found", keyB) 514 } 515 } 516 517 func Test_deleteRoot(t *testing.T) { 518 tree := New[int]() 519 keyA := []byte{'a'} 520 _, _, tree = tree.Insert(keyA, 1) 521 _, _, tree = tree.Delete(keyA) 522 _, _, ok := tree.Get(keyA) 523 if ok { 524 t.Fatal("Root exists") 525 } 526 } 527 528 func Test_deleteIntermediate(t *testing.T) { 529 tree := New[int]() 530 keyA := []byte{'a'} 531 keyAB := []byte{'a', 'b'} 532 keyABC := []byte{'a', 'b', 'c'} 533 _, _, tree = tree.Insert(keyA, 1) 534 _, _, tree = tree.Insert(keyAB, 2) 535 _, _, tree = tree.Insert(keyABC, 3) 536 _, _, tree = tree.Delete(keyAB) 537 _, _, ok := tree.Get(keyA) 538 if !ok { 539 t.Fatal("A doesn't exist") 540 } 541 _, _, ok = tree.Get(keyAB) 542 if ok { 543 t.Fatal("AB exists") 544 } 545 _, _, ok = tree.Get(keyABC) 546 if !ok { 547 t.Fatal("ABC doesn't exist") 548 } 549 } 550 551 func Test_deleteNonExistantIntermediate(t *testing.T) { 552 tree := New[int]() 553 keyAB := []byte{'a', 'b'} 554 keyAC := []byte{'a', 'c'} 555 _, _, tree = tree.Insert(keyAB, 1) 556 _, _, tree = tree.Insert(keyAC, 2) 557 _, _, tree = tree.Delete([]byte{'a'}) 558 _, _, ok := tree.Get(keyAB) 559 if !ok { 560 t.Fatal("AB doesn't exist") 561 } 562 _, _, ok = tree.Get(keyAC) 563 if !ok { 564 t.Fatal("AC doesn't exist") 565 } 566 } 567 568 func Test_deleteNonExistantCommonPrefix(t *testing.T) { 569 tree := New[int]() 570 keyAB := []byte{'a', 'b', 'c'} 571 _, _, tree = tree.Insert(keyAB, 1) 572 _, _, tree = tree.Delete([]byte{'a', 'b', 'e'}) 573 _, _, ok := tree.Get(keyAB) 574 if !ok { 575 t.Fatal("AB doesn't exist") 576 } 577 } 578 579 func Test_replace(t *testing.T) { 580 tree := New[int]() 581 key := binary.BigEndian.AppendUint32(nil, uint32(0)) 582 583 var v int 584 var hadOld bool 585 _, hadOld, tree = tree.Insert(key, 1) 586 require.False(t, hadOld) 587 588 v, hadOld, tree = tree.Insert(key, 2) 589 require.True(t, hadOld) 590 require.EqualValues(t, 1, v) 591 } 592 593 func Test_prefix(t *testing.T) { 594 tree := New[[]byte]() 595 ins := func(s string) { _, _, tree = tree.Insert([]byte(s), []byte(s)) } 596 ins("a") 597 ins("ab") 598 ins("abc") 599 ins("abcd") 600 ins("bc") 601 602 iter, _ := tree.Prefix([]byte("ab")) 603 k, v, ok := iter.Next() 604 assert.True(t, ok) 605 assert.Equal(t, []byte("ab"), k) 606 assert.Equal(t, []byte("ab"), v) 607 608 k, v, ok = iter.Next() 609 assert.True(t, ok) 610 assert.Equal(t, []byte("abc"), k) 611 assert.Equal(t, []byte("abc"), v) 612 613 k, v, ok = iter.Next() 614 assert.True(t, ok) 615 assert.Equal(t, []byte("abcd"), k) 616 assert.Equal(t, []byte("abcd"), v) 617 618 _, _, ok = iter.Next() 619 assert.False(t, ok) 620 } 621 622 func Test_deleteEmptyKey(t *testing.T) { 623 tree := New[string]() 624 625 _, _, tree = tree.Insert([]byte{}, "x") 626 627 v, watch, ok := tree.Get([]byte{}) 628 assert.True(t, ok) 629 assert.Equal(t, "x", v) 630 select { 631 case <-watch: 632 t.Fatalf("channel closed") 633 default: 634 } 635 636 _, _, tree = tree.Delete([]byte{}) 637 638 _, _, ok = tree.Get([]byte{}) 639 assert.False(t, ok) 640 641 select { 642 case <-watch: 643 default: 644 t.Fatalf("channel not closed") 645 } 646 } 647 648 func Test_txn(t *testing.T) { 649 tree := New[uint64]() 650 ins := func(n uint64) { _, _, tree = tree.Insert(uint64Key(n), n) } 651 652 var iter *Iterator[uint64] 653 next := func(exOK bool, exVal int) { 654 t.Helper() 655 _, v, ok := iter.Next() 656 if assert.Equal(t, exOK, ok) { 657 assert.EqualValues(t, exVal, v) 658 } 659 } 660 661 for i := 1; i <= 3; i++ { 662 ins(1) 663 ins(2) 664 ins(3) 665 } 666 667 txn := tree.Txn() 668 txn.Delete(uint64Key(2)) 669 txn.Delete(uint64Key(3)) 670 txn.Insert(uint64Key(4), 4) 671 672 iter = txn.Iterator() 673 next(true, 1) 674 next(true, 4) 675 next(false, 0) 676 677 _ = txn.Commit() // Ignore the new tree 678 679 // Original tree should be untouched. 680 for i := 1; i <= 3; i++ { 681 _, _, ok := tree.Get(uint64Key(uint64(i))) 682 assert.True(t, ok, "Get(%d)", i) 683 } 684 685 iter = tree.Iterator() 686 next(true, 1) 687 next(true, 2) 688 next(true, 3) 689 next(false, 0) 690 } 691 692 func Test_lowerbound(t *testing.T) { 693 tree := New[uint64]() 694 ins := func(n int) { _, _, tree = tree.Insert(uint64Key(uint64(n)), uint64(n)) } 695 696 // Insert 1..3 697 for i := 1; i <= 3; i++ { 698 ins(i) 699 } 700 701 var iter *Iterator[uint64] 702 next := func(exOK bool, exVal int) { 703 t.Helper() 704 _, v, ok := iter.Next() 705 require.Equal(t, exOK, ok) 706 require.EqualValues(t, exVal, v) 707 } 708 709 iter = tree.LowerBound([]byte{}) 710 next(true, 1) 711 next(true, 2) 712 next(true, 3) 713 next(false, 0) 714 715 iter = tree.LowerBound(uint64Key(0)) 716 next(true, 1) 717 next(true, 2) 718 next(true, 3) 719 next(false, 0) 720 721 iter = tree.LowerBound(uint64Key(3)) 722 next(true, 3) 723 next(false, 0) 724 725 iter = tree.LowerBound(uint64Key(4)) 726 next(false, 0) 727 } 728 729 func Test_lowerbound_edge_cases(t *testing.T) { 730 tree := New[uint32]() 731 keys := []uint32{} 732 ins := func(n uint32) { 733 _, _, tree = tree.Insert(uint32Key(n), n) 734 keys = append(keys, n) 735 } 736 737 var iter *Iterator[uint32] 738 next := func(exOK bool, exVal uint32) { 739 t.Helper() 740 _, v, ok := iter.Next() 741 assert.Equal(t, exOK, ok) 742 require.Equal(t, exVal, v) 743 } 744 745 // Empty tree 746 iter = tree.LowerBound([]byte{}) 747 next(false, 0) 748 iter = tree.LowerBound(uint32Key(0x1)) 749 next(false, 0) 750 751 // case 0: Leaf at the root 752 ins(0x1) 753 iter = tree.LowerBound([]byte{}) 754 next(true, 0x1) 755 next(false, 0) 756 iter = tree.LowerBound(uint32Key(0x1)) 757 next(true, 0x1) 758 next(false, 0) 759 iter = tree.LowerBound(uint32Key(0x2)) 760 next(false, 0) 761 762 // Two leafs, node4 root 763 ins(0x2) 764 iter = tree.LowerBound([]byte{}) 765 next(true, 0x1) 766 next(true, 0x2) 767 next(false, 0) 768 iter = tree.LowerBound(uint32Key(0x2)) 769 next(true, 0x2) 770 next(false, 0) 771 iter = tree.LowerBound(uint32Key(0x3)) 772 next(false, 0) 773 774 // Different prefix 775 ins(0x0101) 776 iter = tree.LowerBound(uint32Key(0x100)) 777 next(true, 0x101) 778 next(false, 0) 779 780 // case -1: Matching prefix (0x1??) but only smaller nodes behind it 781 ins(0x1100) 782 iter = tree.LowerBound(uint32Key(0x102)) 783 next(true, 0x1100) 784 next(false, 0) 785 786 // Short search keys 787 ins(0x010000) 788 789 iter = tree.LowerBound([]byte{1}) 790 next(false, 0) 791 792 iter = tree.LowerBound([]byte{0, 0}) 793 next(true, 0x1) 794 next(true, 0x2) 795 next(true, 0x0101) 796 next(true, 0x1100) 797 next(true, 0x010000) 798 next(false, 0) 799 800 iter = tree.LowerBound([]byte{0, 1, 0}) 801 next(true, 0x010000) 802 next(false, 0) 803 804 // Node256 805 for i := 1; i < 50; i += 2 { // add less than 256 for some holes in node256.children 806 n := uint32(0x20000 + i) 807 _, _, tree = tree.Insert(uint32Key(n), n) 808 keys = append(keys, n) 809 } 810 811 iter = tree.LowerBound(uint32Key(0x20000)) 812 for i := 1; i < 50; i += 2 { 813 n := uint32(0x20000 + i) 814 next(true, n) 815 } 816 next(false, 0) 817 818 iter = tree.LowerBound([]byte{}) 819 for i := range keys { 820 next(true, keys[i]) 821 } 822 next(false, 0) 823 824 } 825 826 func Test_lowerbound_regression(t *testing.T) { 827 // Regression test for bug in lowerbound() where the lowerbound search ended up 828 // in a smaller node and thought there were no larger nodes in the tree to iterate 829 // over. 830 831 tree := New[uint64]() 832 ins := func(n uint64) { _, _, tree = tree.Insert(uint64Key(uint64(n)), uint64(n)) } 833 834 values := []uint64{ 835 70370, // ... 1 18 226 836 70411, // ... 1 19 11 837 70412, 838 } 839 840 for _, v := range values { 841 ins(v) 842 } 843 844 iter := tree.LowerBound(uint64Key(70399)) 845 i := 1 846 for _, obj, ok := iter.Next(); ok; _, obj, ok = iter.Next() { 847 require.Equal(t, values[i], obj) 848 i++ 849 } 850 require.Equal(t, len(values), i) 851 } 852 853 func Test_prefix_regression(t *testing.T) { 854 // Regression test for bug where a long key and a short key was inserted and where 855 // the keys shared a prefix. 856 857 tree := New[string]() 858 _, _, tree = tree.Insert([]byte("foobar"), "foobar") 859 _, _, tree = tree.Insert([]byte("foo"), "foo") 860 861 s, _, found := tree.Get([]byte("foobar")) 862 require.True(t, found) 863 require.Equal(t, s, "foobar") 864 865 s, _, found = tree.Get([]byte("foo")) 866 require.True(t, found) 867 require.Equal(t, s, "foo") 868 } 869 870 func Test_iterate(t *testing.T) { 871 sizes := []int{1, 10, 100, 1000} 872 for _, size := range sizes { 873 t.Logf("size=%d", size) 874 tree := New[uint64]() 875 keys := []uint64{} 876 for i := 0; i < size; i++ { 877 keys = append(keys, uint64(i)) 878 } 879 880 rand.Shuffle(len(keys), func(i, j int) { 881 keys[i], keys[j] = keys[j], keys[i] 882 }) 883 884 watches := []<-chan struct{}{} 885 for _, i := range keys { 886 _, _, tree = tree.Insert(hexKey(uint64(i)), uint64(i)) 887 v, watch, ok := tree.Get(hexKey(uint64(i))) 888 require.True(t, ok, "Get %x", hexKey(uint64(i))) 889 require.Equal(t, v, uint64(i), "values equal") 890 require.NotNil(t, watch, "watch not nil") 891 watches = append(watches, watch) 892 } 893 894 // Check that watches are not closed. 895 for _, w := range watches { 896 select { 897 case <-w: 898 tree.PrintTree() 899 t.Fatalf("watch channel %p closed unexpectedly", w) 900 default: 901 } 902 } 903 904 // Insert again and validate that the old value is returned and 905 // all watch channels are closed. 906 for _, i := range keys { 907 var old uint64 908 var hadOld bool 909 old, hadOld, tree = tree.Insert(hexKey(uint64(i)), uint64(i)) 910 assert.True(t, hadOld, "hadOld") 911 assert.Equal(t, old, uint64(i)) 912 } 913 t.Logf("waiting for watches to close") 914 for _, w := range watches { 915 <-w 916 } 917 918 // The order for the variable length keys is based on prefix, 919 // so we would get 0x0105 before 0x02, since it has "smaller" 920 // prefix. Hence we just check we see all values. 921 iter := tree.Iterator() 922 i := int(0) 923 for key, obj, ok := iter.Next(); ok; key, obj, ok = iter.Next() { 924 if !bytes.Equal(hexKey(obj), key) { 925 t.Fatalf("expected %x, got %x", key, hexKey(obj)) 926 } 927 i++ 928 } 929 if !assert.Equal(t, size, i) { 930 tree.PrintTree() 931 t.FailNow() 932 } 933 934 _, _, ok := iter.Next() 935 require.False(t, ok, "expected exhausted iterator to keep returning false") 936 937 // Delete keys one at a time, in random order. 938 rand.Shuffle(len(keys), func(i, j int) { 939 keys[i], keys[j] = keys[j], keys[i] 940 }) 941 txn := tree.Txn() 942 n := rand.Intn(20) 943 for i, k := range keys { 944 txn.Delete(hexKey(uint64(k))) 945 946 n-- 947 if n <= 0 { 948 tree = txn.Commit() 949 txn = tree.Txn() 950 n = rand.Intn(20) 951 } 952 953 // All the rest of the keys can still be found 954 for _, j := range keys[i+1:] { 955 n, _, found := txn.Get(hexKey(j)) 956 if !assert.True(t, found) || !assert.Equal(t, n, j) { 957 fmt.Println("--- new tree") 958 txn.PrintTree() 959 t.FailNow() 960 } 961 } 962 } 963 964 } 965 } 966 967 func Test_closed_chan_regression(t *testing.T) { 968 tree := New[uint64]() 969 _, _, tree = tree.Insert(hexKey(uint64(0)), uint64(0)) 970 _, _, tree = tree.Insert(hexKey(uint64(1)), uint64(1)) 971 _, _, tree = tree.Insert(hexKey(uint64(2)), uint64(2)) 972 _, _, tree = tree.Insert(hexKey(uint64(3)), uint64(3)) 973 974 txn := tree.Txn() 975 txn.Delete(hexKey(uint64(3))) 976 txn.Delete(hexKey(uint64(1))) 977 tree = txn.Commit() 978 979 // No reachable channel should be closed 980 for _, c := range tree.root.children() { 981 select { 982 case <-c.watch: 983 t.Logf("%x %p closed already", c.prefix, &c.watch) 984 t.FailNow() 985 default: 986 } 987 } 988 } 989 990 func Test_lowerbound_bigger(t *testing.T) { 991 tree := New[uint64]() 992 ins := func(n int) { _, _, tree = tree.Insert(uint64Key(uint64(n)), uint64(n)) } 993 994 // Insert 5..10 995 for i := 5; i <= 10; i++ { 996 ins(i) 997 } 998 999 iter := tree.LowerBound([]byte{4}) 1000 _, _, ok := iter.Next() 1001 require.False(t, ok) 1002 } 1003 1004 func Benchmark_Insert_RootOnlyWatch(b *testing.B) { 1005 benchmark_Insert(b, RootOnlyWatch) 1006 } 1007 1008 func Benchmark_Insert(b *testing.B) { 1009 benchmark_Insert(b) 1010 } 1011 1012 func benchmark_Insert(b *testing.B, opts ...Option) { 1013 for n := 0; n < b.N; n++ { 1014 tree := New[int](opts...) 1015 txn := tree.Txn() 1016 for i := 0; i < numObjectsToInsert; i++ { 1017 key := binary.BigEndian.AppendUint32(nil, uint32(numObjectsToInsert+i)) 1018 txn.Insert(key, numObjectsToInsert+i) 1019 } 1020 txn.Commit() 1021 } 1022 b.StopTimer() 1023 b.ReportMetric(float64(b.N*numObjectsToInsert)/b.Elapsed().Seconds(), "objects/sec") 1024 } 1025 1026 func benchmark_Modify_vs_GetInsert(b *testing.B, doGetInsert bool) { 1027 tree := New[int](RootOnlyWatch) 1028 keys := [][]byte{} 1029 for i := 0; i < numObjectsToInsert; i++ { 1030 key := binary.BigEndian.AppendUint32(nil, uint32(numObjectsToInsert+i)) 1031 _, _, tree = tree.Insert(key, numObjectsToInsert+i) 1032 keys = append(keys, key) 1033 } 1034 b.ResetTimer() 1035 for n := 0; n < b.N; n++ { 1036 txn := tree.Txn() 1037 for _, key := range keys { 1038 if doGetInsert { 1039 v, _, _ := txn.Get(key) 1040 txn.Insert(key, v) 1041 } else { 1042 txn.Modify(key, func(x int) int { return x }) 1043 } 1044 } 1045 tree = txn.Commit() 1046 } 1047 b.ReportMetric(float64(b.N*numObjectsToInsert)/b.Elapsed().Seconds(), "objects/sec") 1048 } 1049 1050 func Benchmark_Modify(b *testing.B) { 1051 benchmark_Modify_vs_GetInsert(b, false) 1052 } 1053 1054 func Benchmark_GetInsert(b *testing.B) { 1055 benchmark_Modify_vs_GetInsert(b, true) 1056 } 1057 1058 func Benchmark_Replace(b *testing.B) { 1059 benchmark_Replace(b, true) 1060 } 1061 1062 func Benchmark_Replace_RootOnlyWatch(b *testing.B) { 1063 benchmark_Replace(b, false) 1064 } 1065 1066 func benchmark_Replace(b *testing.B, watching bool) { 1067 tree := New[int](RootOnlyWatch) 1068 txn := tree.Txn() 1069 for i := 0; i < numObjectsToInsert; i++ { 1070 key := binary.BigEndian.AppendUint32(nil, uint32(numObjectsToInsert+i)) 1071 txn.Insert(key, numObjectsToInsert+i) 1072 } 1073 1074 b.ResetTimer() 1075 key := binary.BigEndian.AppendUint32(nil, uint32(0)) 1076 for n := 0; n < b.N; n++ { 1077 txn.Insert(key, 0) 1078 } 1079 b.StopTimer() 1080 b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "objects/sec") 1081 } 1082 1083 func Benchmark_txn_1(b *testing.B) { 1084 benchmark_txn_batch(b, 1) 1085 } 1086 1087 func Benchmark_txn_10(b *testing.B) { 1088 benchmark_txn_batch(b, 10) 1089 } 1090 1091 func Benchmark_txn_100(b *testing.B) { 1092 benchmark_txn_batch(b, 100) 1093 } 1094 1095 func Benchmark_txn_1000(b *testing.B) { 1096 benchmark_txn_batch(b, 1000) 1097 } 1098 1099 func benchmark_txn_batch(b *testing.B, batchSize int) { 1100 tree := New[int](RootOnlyWatch) 1101 n := b.N 1102 for n > 0 { 1103 txn := tree.Txn() 1104 for j := 0; j < batchSize; j++ { 1105 txn.Insert(uint64Key(uint64(j)), j) 1106 } 1107 tree = txn.Commit() 1108 n -= batchSize 1109 } 1110 txn := tree.Txn() 1111 for j := 0; j < n; j++ { 1112 txn.Insert(uint64Key(uint64(j)), j) 1113 } 1114 txn.Commit() 1115 b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "objects/sec") 1116 } 1117 1118 func Benchmark_txn_delete_1(b *testing.B) { 1119 benchmark_txn_delete_batch(b, 1) 1120 } 1121 1122 func Benchmark_txn_delete_10(b *testing.B) { 1123 benchmark_txn_delete_batch(b, 10) 1124 } 1125 1126 func Benchmark_txn_delete_100(b *testing.B) { 1127 benchmark_txn_delete_batch(b, 100) 1128 } 1129 1130 func Benchmark_txn_delete_1000(b *testing.B) { 1131 benchmark_txn_delete_batch(b, 1000) 1132 } 1133 1134 func benchmark_txn_delete_batch(b *testing.B, batchSize int) { 1135 tree := New[int](RootOnlyWatch) 1136 for j := 0; j < batchSize; j++ { 1137 _, _, tree = tree.Insert(uint64Key(uint64(j)), j) 1138 } 1139 b.ResetTimer() 1140 1141 n := b.N 1142 for n > 0 { 1143 txn := tree.Txn() 1144 for j := 0; j < batchSize; j++ { 1145 txn.Delete(uint64Key(uint64(j))) 1146 } 1147 n -= batchSize 1148 } 1149 txn := tree.Txn() 1150 for j := 0; j < n; j++ { 1151 txn.Delete(uint64Key(uint64(j))) 1152 } 1153 b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "objects/sec") 1154 } 1155 1156 func Benchmark_Get(b *testing.B) { 1157 tree := New[uint64](RootOnlyWatch) 1158 for j := uint64(0); j < numObjectsToInsert; j++ { 1159 _, _, tree = tree.Insert(uint64Key(j), j) 1160 } 1161 b.ResetTimer() 1162 var key [8]byte // to avoid the allocation 1163 for i := 0; i < b.N; i++ { 1164 for j := uint64(0); j < numObjectsToInsert; j++ { 1165 binary.BigEndian.PutUint64(key[:], j) 1166 v, _, ok := tree.Get(key[:]) 1167 if v != j { 1168 b.Fatalf("impossible: %d != %d || %v", v, j, ok) 1169 } 1170 } 1171 1172 } 1173 b.ReportMetric(float64(numObjectsToInsert*b.N)/b.Elapsed().Seconds(), "objects/sec") 1174 } 1175 1176 func Benchmark_Iterate(b *testing.B) { 1177 tree := New[uint64](RootOnlyWatch) 1178 for j := uint64(1); j <= numObjectsToInsert; j++ { 1179 _, _, tree = tree.Insert(uint64Key(j), j) 1180 } 1181 b.ResetTimer() 1182 for i := 0; i < b.N; i++ { 1183 iter := tree.Iterator() 1184 for _, j, ok := iter.Next(); ok; _, j, ok = iter.Next() { 1185 if j < 1 || j > numObjectsToInsert+1 { 1186 b.Fatalf("impossible value: %d", j) 1187 } 1188 } 1189 } 1190 b.ReportMetric(float64(numObjectsToInsert*b.N)/b.Elapsed().Seconds(), "objects/sec") 1191 } 1192 1193 func Benchmark_Hashmap_Insert(b *testing.B) { 1194 for i := 0; i < b.N; i++ { 1195 m := map[uint64]uint64{} 1196 for j := uint64(0); j < numObjectsToInsert; j++ { 1197 m[j] = j 1198 } 1199 if len(m) != numObjectsToInsert { 1200 b.Fatalf("%d != %d", len(m), numObjectsToInsert) 1201 } 1202 } 1203 b.ReportMetric(float64(numObjectsToInsert*b.N)/b.Elapsed().Seconds(), "objects/sec") 1204 } 1205 1206 func Benchmark_Hashmap_Get_Uint64(b *testing.B) { 1207 m := map[uint64]uint64{} 1208 for j := uint64(0); j < numObjectsToInsert; j++ { 1209 m[j] = j 1210 } 1211 b.ResetTimer() 1212 for i := 0; i < b.N; i++ { 1213 for j := uint64(0); j < numObjectsToInsert; j++ { 1214 if m[j] != j { 1215 b.Fatalf("impossible: %d != %d", m[j], j) 1216 } 1217 } 1218 } 1219 b.ReportMetric(float64(numObjectsToInsert*b.N)/b.Elapsed().Seconds(), "objects/sec") 1220 } 1221 1222 func Benchmark_Hashmap_Get_Bytes(b *testing.B) { 1223 var k [8]byte 1224 m := map[[8]byte]uint64{} 1225 for j := uint64(0); j < numObjectsToInsert; j++ { 1226 binary.BigEndian.PutUint64(k[:], j) 1227 m[k] = j 1228 } 1229 b.ResetTimer() 1230 for i := 0; i < b.N; i++ { 1231 for j := uint64(0); j < numObjectsToInsert; j++ { 1232 binary.BigEndian.PutUint64(k[:], j) 1233 if m[k] != j { 1234 b.Fatalf("impossible: %d != %d", m[k], j) 1235 } 1236 } 1237 } 1238 b.ReportMetric(float64(numObjectsToInsert*b.N)/b.Elapsed().Seconds(), "objects/sec") 1239 }