github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/lsmkv/strategies_map_integration_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 //go:build integrationTest 13 // +build integrationTest 14 15 package lsmkv 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "github.com/weaviate/weaviate/entities/cyclemanager" 25 ) 26 27 func TestMapCollectionStrategy(t *testing.T) { 28 ctx := testCtx() 29 tests := bucketIntegrationTests{ 30 { 31 name: "mapInsertAndAppend", 32 f: mapInsertAndAppend, 33 opts: []BucketOption{ 34 WithStrategy(StrategyMapCollection), 35 }, 36 }, 37 { 38 name: "mapInsertAndDelete", 39 f: mapInsertAndDelete, 40 opts: []BucketOption{ 41 WithStrategy(StrategyMapCollection), 42 }, 43 }, 44 { 45 name: "mapCursors", 46 f: mapCursors, 47 opts: []BucketOption{ 48 WithStrategy(StrategyMapCollection), 49 }, 50 }, 51 } 52 tests.run(ctx, t) 53 } 54 55 func mapInsertAndAppend(ctx context.Context, t *testing.T, opts []BucketOption) { 56 dirName := t.TempDir() 57 58 t.Run("memtable-only", func(t *testing.T) { 59 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 60 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 61 require.Nil(t, err) 62 63 // so big it effectively never triggers as part of this test 64 b.SetMemtableThreshold(1e9) 65 66 rowKey1 := []byte("test1-key-1") 67 rowKey2 := []byte("test1-key-2") 68 69 t.Run("set original values and verify", func(t *testing.T) { 70 row1Map := []MapPair{ 71 { 72 Key: []byte("row1-key1"), 73 Value: []byte("row1-key1-value1"), 74 }, { 75 Key: []byte("row1-key2"), 76 Value: []byte("row1-key2-value1"), 77 }, 78 } 79 80 row2Map := []MapPair{ 81 { 82 Key: []byte("row2-key1"), 83 Value: []byte("row2-key1-value1"), 84 }, { 85 Key: []byte("row2-key2"), 86 Value: []byte("row2-key2-value1"), 87 }, 88 } 89 90 for _, pair := range row1Map { 91 err = b.MapSet(rowKey1, pair) 92 require.Nil(t, err) 93 } 94 95 for _, pair := range row2Map { 96 err = b.MapSet(rowKey2, pair) 97 require.Nil(t, err) 98 } 99 100 res, err := b.MapList(rowKey1) 101 require.Nil(t, err) 102 assert.Equal(t, row1Map, res) 103 res, err = b.MapList(rowKey2) 104 require.Nil(t, err) 105 assert.Equal(t, res, row2Map) 106 }) 107 108 t.Run("replace an existing map key", func(t *testing.T) { 109 err = b.MapSet(rowKey1, MapPair{ 110 Key: []byte("row1-key1"), // existing key 111 Value: []byte("row1-key1-value2"), // updated value 112 }) 113 require.Nil(t, err) 114 115 row1Updated := []MapPair{ 116 { 117 Key: []byte("row1-key1"), 118 Value: []byte("row1-key1-value2"), // <--- updated, rest unchanged 119 }, { 120 Key: []byte("row1-key2"), 121 Value: []byte("row1-key2-value1"), 122 }, 123 } 124 125 row2Unchanged := []MapPair{ 126 { 127 Key: []byte("row2-key1"), 128 Value: []byte("row2-key1-value1"), 129 }, { 130 Key: []byte("row2-key2"), 131 Value: []byte("row2-key2-value1"), 132 }, 133 } 134 135 res, err := b.MapList(rowKey1) 136 require.Nil(t, err) 137 // NOTE: We are accepting that the order is changed here. Given the name 138 // "MapCollection" there should be no expectations regarding the order, 139 // but we have yet to validate if this fits with all of the intended use 140 // cases. 141 assert.ElementsMatch(t, row1Updated, res) 142 res, err = b.MapList(rowKey2) 143 require.Nil(t, err) 144 assert.Equal(t, res, row2Unchanged) 145 }) 146 }) 147 148 t.Run("with a single flush between updates", func(t *testing.T) { 149 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 150 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 151 require.Nil(t, err) 152 153 // so big it effectively never triggers as part of this test 154 b.SetMemtableThreshold(1e9) 155 156 rowKey1 := []byte("test2-key-1") 157 rowKey2 := []byte("test2-key-2") 158 159 t.Run("set original values and verify", func(t *testing.T) { 160 row1Map := []MapPair{ 161 { 162 Key: []byte("row1-key1"), 163 Value: []byte("row1-key1-value1"), 164 }, { 165 Key: []byte("row1-key2"), 166 Value: []byte("row1-key2-value1"), 167 }, 168 } 169 170 row2Map := []MapPair{ 171 { 172 Key: []byte("row2-key1"), 173 Value: []byte("row2-key1-value1"), 174 }, { 175 Key: []byte("row2-key2"), 176 Value: []byte("row2-key2-value1"), 177 }, 178 } 179 180 for _, pair := range row1Map { 181 err = b.MapSet(rowKey1, pair) 182 require.Nil(t, err) 183 } 184 185 for _, pair := range row2Map { 186 err = b.MapSet(rowKey2, pair) 187 require.Nil(t, err) 188 } 189 190 res, err := b.MapList(rowKey1) 191 require.Nil(t, err) 192 assert.Equal(t, row1Map, res) 193 res, err = b.MapList(rowKey2) 194 require.Nil(t, err) 195 assert.Equal(t, res, row2Map) 196 }) 197 198 t.Run("flush to disk", func(t *testing.T) { 199 require.Nil(t, b.FlushAndSwitch()) 200 }) 201 202 t.Run("replace an existing map key", func(t *testing.T) { 203 err = b.MapSet(rowKey1, MapPair{ 204 Key: []byte("row1-key1"), // existing key 205 Value: []byte("row1-key1-value2"), // updated value 206 }) 207 require.Nil(t, err) 208 209 row1Updated := []MapPair{ 210 { 211 Key: []byte("row1-key1"), 212 Value: []byte("row1-key1-value2"), // <--- updated, rest unchanged 213 }, { 214 Key: []byte("row1-key2"), 215 Value: []byte("row1-key2-value1"), 216 }, 217 } 218 219 row2Unchanged := []MapPair{ 220 { 221 Key: []byte("row2-key1"), 222 Value: []byte("row2-key1-value1"), 223 }, { 224 Key: []byte("row2-key2"), 225 Value: []byte("row2-key2-value1"), 226 }, 227 } 228 229 res, err := b.MapList(rowKey1) 230 require.Nil(t, err) 231 // NOTE: We are accepting that the order is changed here. Given the name 232 // "MapCollection" there should be no expectations regarding the order, 233 // but we have yet to validate if this fits with all of the intended use 234 // cases. 235 assert.ElementsMatch(t, row1Updated, res) 236 res, err = b.MapList(rowKey2) 237 require.Nil(t, err) 238 assert.Equal(t, res, row2Unchanged) 239 }) 240 }) 241 242 t.Run("with flushes after initial and update", func(t *testing.T) { 243 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 244 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 245 require.Nil(t, err) 246 247 // so big it effectively never triggers as part of this test 248 b.SetMemtableThreshold(1e9) 249 250 rowKey1 := []byte("test3-key-1") 251 rowKey2 := []byte("test3-key-2") 252 253 t.Run("set original values and verify", func(t *testing.T) { 254 row1Map := []MapPair{ 255 { 256 Key: []byte("row1-key1"), 257 Value: []byte("row1-key1-value1"), 258 }, { 259 Key: []byte("row1-key2"), 260 Value: []byte("row1-key2-value1"), 261 }, 262 } 263 264 row2Map := []MapPair{ 265 { 266 Key: []byte("row2-key1"), 267 Value: []byte("row2-key1-value1"), 268 }, { 269 Key: []byte("row2-key2"), 270 Value: []byte("row2-key2-value1"), 271 }, 272 } 273 274 for _, pair := range row1Map { 275 err = b.MapSet(rowKey1, pair) 276 require.Nil(t, err) 277 } 278 279 for _, pair := range row2Map { 280 err = b.MapSet(rowKey2, pair) 281 require.Nil(t, err) 282 } 283 284 res, err := b.MapList(rowKey1) 285 require.Nil(t, err) 286 assert.Equal(t, row1Map, res) 287 res, err = b.MapList(rowKey2) 288 require.Nil(t, err) 289 assert.Equal(t, res, row2Map) 290 }) 291 292 t.Run("flush to disk", func(t *testing.T) { 293 require.Nil(t, b.FlushAndSwitch()) 294 }) 295 296 t.Run("replace an existing map key", func(t *testing.T) { 297 err = b.MapSet(rowKey1, MapPair{ 298 Key: []byte("row1-key1"), // existing key 299 Value: []byte("row1-key1-value2"), // updated value 300 }) 301 require.Nil(t, err) 302 303 // Flush again! 304 require.Nil(t, b.FlushAndSwitch()) 305 306 row1Updated := []MapPair{ 307 { 308 Key: []byte("row1-key1"), 309 Value: []byte("row1-key1-value2"), // <--- updated, rest unchanged 310 }, { 311 Key: []byte("row1-key2"), 312 Value: []byte("row1-key2-value1"), 313 }, 314 } 315 316 row2Unchanged := []MapPair{ 317 { 318 Key: []byte("row2-key1"), 319 Value: []byte("row2-key1-value1"), 320 }, { 321 Key: []byte("row2-key2"), 322 Value: []byte("row2-key2-value1"), 323 }, 324 } 325 326 res, err := b.MapList(rowKey1) 327 require.Nil(t, err) 328 // NOTE: We are accepting that the order is changed here. Given the name 329 // "MapCollection" there should be no expectations regarding the order, 330 // but we have yet to validate if this fits with all of the intended use 331 // cases. 332 assert.ElementsMatch(t, row1Updated, res) 333 res, err = b.MapList(rowKey2) 334 require.Nil(t, err) 335 assert.Equal(t, res, row2Unchanged) 336 }) 337 }) 338 339 t.Run("update in memtable, then do an orderly shutdown, and re-init", func(t *testing.T) { 340 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 341 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 342 require.Nil(t, err) 343 344 // so big it effectively never triggers as part of this test 345 b.SetMemtableThreshold(1e9) 346 347 rowKey1 := []byte("test4-key-1") 348 rowKey2 := []byte("test4-key-2") 349 350 t.Run("set original values and verify", func(t *testing.T) { 351 row1Map := []MapPair{ 352 { 353 Key: []byte("row1-key1"), 354 Value: []byte("row1-key1-value1"), 355 }, { 356 Key: []byte("row1-key2"), 357 Value: []byte("row1-key2-value1"), 358 }, 359 } 360 361 row2Map := []MapPair{ 362 { 363 Key: []byte("row2-key1"), 364 Value: []byte("row2-key1-value1"), 365 }, { 366 Key: []byte("row2-key2"), 367 Value: []byte("row2-key2-value1"), 368 }, 369 } 370 371 for _, pair := range row1Map { 372 err = b.MapSet(rowKey1, pair) 373 require.Nil(t, err) 374 } 375 376 for _, pair := range row2Map { 377 err = b.MapSet(rowKey2, pair) 378 require.Nil(t, err) 379 } 380 381 res, err := b.MapList(rowKey1) 382 require.Nil(t, err) 383 assert.Equal(t, row1Map, res) 384 res, err = b.MapList(rowKey2) 385 require.Nil(t, err) 386 assert.Equal(t, res, row2Map) 387 }) 388 389 t.Run("replace an existing map key", func(t *testing.T) { 390 err = b.MapSet(rowKey1, MapPair{ 391 Key: []byte("row1-key1"), // existing key 392 Value: []byte("row1-key1-value2"), // updated value 393 }) 394 require.Nil(t, err) 395 }) 396 397 t.Run("orderly shutdown", func(t *testing.T) { 398 b.Shutdown(context.Background()) 399 }) 400 401 t.Run("init another bucket on the same files", func(t *testing.T) { 402 b2, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 403 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 404 require.Nil(t, err) 405 406 row1Updated := []MapPair{ 407 { 408 Key: []byte("row1-key1"), 409 Value: []byte("row1-key1-value2"), // <--- updated, rest unchanged 410 }, { 411 Key: []byte("row1-key2"), 412 Value: []byte("row1-key2-value1"), 413 }, 414 } 415 416 row2Unchanged := []MapPair{ 417 { 418 Key: []byte("row2-key1"), 419 Value: []byte("row2-key1-value1"), 420 }, { 421 Key: []byte("row2-key2"), 422 Value: []byte("row2-key2-value1"), 423 }, 424 } 425 426 res, err := b2.MapList(rowKey1) 427 require.Nil(t, err) 428 // NOTE: We are accepting that the order is changed here. Given the name 429 // "MapCollection" there should be no expectations regarding the order, 430 // but we have yet to validate if this fits with all of the intended use 431 // cases. 432 assert.ElementsMatch(t, row1Updated, res) 433 res, err = b2.MapList(rowKey2) 434 require.Nil(t, err) 435 assert.Equal(t, res, row2Unchanged) 436 }) 437 }) 438 } 439 440 func mapInsertAndDelete(ctx context.Context, t *testing.T, opts []BucketOption) { 441 dirName := t.TempDir() 442 443 t.Run("memtable-only", func(t *testing.T) { 444 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 445 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 446 require.Nil(t, err) 447 448 // so big it effectively never triggers as part of this test 449 b.SetMemtableThreshold(1e9) 450 451 rowKey1 := []byte("test1-key-1") 452 rowKey2 := []byte("test1-key-2") 453 454 t.Run("set original values and verify", func(t *testing.T) { 455 row1Map := []MapPair{ 456 { 457 Key: []byte("row1-key1"), 458 Value: []byte("row1-key1-value1"), 459 }, { 460 Key: []byte("row1-key2"), 461 Value: []byte("row1-key2-value1"), 462 }, 463 } 464 465 row2Map := []MapPair{ 466 { 467 Key: []byte("row2-key1"), 468 Value: []byte("row2-key1-value1"), 469 }, { 470 Key: []byte("row2-key2"), 471 Value: []byte("row2-key2-value1"), 472 }, 473 } 474 475 for _, pair := range row1Map { 476 err = b.MapSet(rowKey1, pair) 477 require.Nil(t, err) 478 } 479 480 for _, pair := range row2Map { 481 err = b.MapSet(rowKey2, pair) 482 require.Nil(t, err) 483 } 484 485 res, err := b.MapList(rowKey1) 486 require.Nil(t, err) 487 assert.Equal(t, row1Map, res) 488 res, err = b.MapList(rowKey2) 489 require.Nil(t, err) 490 assert.Equal(t, res, row2Map) 491 }) 492 493 t.Run("delete some keys, re-add one of them", func(t *testing.T) { 494 err := b.MapDeleteKey(rowKey1, []byte("row1-key1")) 495 require.Nil(t, err) 496 err = b.MapDeleteKey(rowKey2, []byte("row2-key2")) 497 require.Nil(t, err) 498 err = b.MapSet(rowKey2, MapPair{ 499 Key: []byte("row2-key2"), 500 Value: []byte("row2-key2-reinserted"), 501 }) 502 require.Nil(t, err) 503 }) 504 505 t.Run("validate the results", func(t *testing.T) { 506 row1Updated := []MapPair{ 507 // key 1 was deleted 508 { 509 Key: []byte("row1-key2"), 510 Value: []byte("row1-key2-value1"), 511 }, 512 } 513 514 row2Updated := []MapPair{ 515 { 516 Key: []byte("row2-key1"), 517 Value: []byte("row2-key1-value1"), 518 }, { 519 Key: []byte("row2-key2"), 520 Value: []byte("row2-key2-reinserted"), 521 }, 522 } 523 524 // NOTE: We are accepting that the order is changed here. Given the name 525 // "MapCollection" there should be no expectations regarding the order, 526 // but we have yet to validate if this fits with all of the intended use 527 // cases. 528 res, err := b.MapList(rowKey1) 529 require.Nil(t, err) 530 assert.ElementsMatch(t, row1Updated, res) 531 res, err = b.MapList(rowKey2) 532 require.Nil(t, err) 533 assert.ElementsMatch(t, row2Updated, res) 534 }) 535 }) 536 537 t.Run("with flushes between updates", func(t *testing.T) { 538 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 539 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 540 require.Nil(t, err) 541 542 // so big it effectively never triggers as part of this test 543 b.SetMemtableThreshold(1e9) 544 545 rowKey1 := []byte("test1-key-1") 546 rowKey2 := []byte("test1-key-2") 547 548 t.Run("set original values and verify", func(t *testing.T) { 549 row1Map := []MapPair{ 550 { 551 Key: []byte("row1-key1"), 552 Value: []byte("row1-key1-value1"), 553 }, { 554 Key: []byte("row1-key2"), 555 Value: []byte("row1-key2-value1"), 556 }, 557 } 558 559 row2Map := []MapPair{ 560 { 561 Key: []byte("row2-key1"), 562 Value: []byte("row2-key1-value1"), 563 }, { 564 Key: []byte("row2-key2"), 565 Value: []byte("row2-key2-value1"), 566 }, 567 } 568 569 for _, pair := range row1Map { 570 err = b.MapSet(rowKey1, pair) 571 require.Nil(t, err) 572 } 573 574 for _, pair := range row2Map { 575 err = b.MapSet(rowKey2, pair) 576 require.Nil(t, err) 577 } 578 579 res, err := b.MapList(rowKey1) 580 require.Nil(t, err) 581 assert.Equal(t, row1Map, res) 582 res, err = b.MapList(rowKey2) 583 require.Nil(t, err) 584 assert.Equal(t, res, row2Map) 585 }) 586 587 t.Run("flush to disk", func(t *testing.T) { 588 require.Nil(t, b.FlushAndSwitch()) 589 }) 590 591 t.Run("delete some keys, re-add one of them", func(t *testing.T) { 592 err := b.MapDeleteKey(rowKey1, []byte("row1-key1")) 593 require.Nil(t, err) 594 err = b.MapDeleteKey(rowKey2, []byte("row2-key2")) 595 require.Nil(t, err) 596 err = b.MapSet(rowKey2, MapPair{ 597 Key: []byte("row2-key2"), 598 Value: []byte("row2-key2-reinserted"), 599 }) 600 require.Nil(t, err) 601 }) 602 603 t.Run("flush to disk", func(t *testing.T) { 604 require.Nil(t, b.FlushAndSwitch()) 605 }) 606 607 t.Run("validate the results", func(t *testing.T) { 608 row1Updated := []MapPair{ 609 // key 1 was deleted 610 { 611 Key: []byte("row1-key2"), 612 Value: []byte("row1-key2-value1"), 613 }, 614 } 615 616 row2Updated := []MapPair{ 617 { 618 Key: []byte("row2-key1"), 619 Value: []byte("row2-key1-value1"), 620 }, { 621 Key: []byte("row2-key2"), 622 Value: []byte("row2-key2-reinserted"), 623 }, 624 } 625 626 // NOTE: We are accepting that the order is changed here. Given the name 627 // "MapCollection" there should be no expectations regarding the order, 628 // but we have yet to validate if this fits with all of the intended use 629 // cases. 630 res, err := b.MapList(rowKey1) 631 require.Nil(t, err) 632 assert.ElementsMatch(t, row1Updated, res) 633 res, err = b.MapList(rowKey2) 634 require.Nil(t, err) 635 assert.ElementsMatch(t, row2Updated, res) 636 }) 637 }) 638 639 t.Run("with memtable only, then an orderly shutdown and restart", func(t *testing.T) { 640 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 641 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 642 require.Nil(t, err) 643 644 // so big it effectively never triggers as part of this test 645 b.SetMemtableThreshold(1e9) 646 647 rowKey1 := []byte("test1-key-1") 648 rowKey2 := []byte("test1-key-2") 649 650 t.Run("set original values and verify", func(t *testing.T) { 651 row1Map := []MapPair{ 652 { 653 Key: []byte("row1-key1"), 654 Value: []byte("row1-key1-value1"), 655 }, { 656 Key: []byte("row1-key2"), 657 Value: []byte("row1-key2-value1"), 658 }, 659 } 660 661 row2Map := []MapPair{ 662 { 663 Key: []byte("row2-key1"), 664 Value: []byte("row2-key1-value1"), 665 }, { 666 Key: []byte("row2-key2"), 667 Value: []byte("row2-key2-value1"), 668 }, 669 } 670 671 for _, pair := range row1Map { 672 err = b.MapSet(rowKey1, pair) 673 require.Nil(t, err) 674 } 675 676 for _, pair := range row2Map { 677 err = b.MapSet(rowKey2, pair) 678 require.Nil(t, err) 679 } 680 681 res, err := b.MapList(rowKey1) 682 require.Nil(t, err) 683 assert.Equal(t, row1Map, res) 684 res, err = b.MapList(rowKey2) 685 require.Nil(t, err) 686 assert.Equal(t, res, row2Map) 687 }) 688 689 t.Run("delete some keys, re-add one of them", func(t *testing.T) { 690 err := b.MapDeleteKey(rowKey1, []byte("row1-key1")) 691 require.Nil(t, err) 692 err = b.MapDeleteKey(rowKey2, []byte("row2-key2")) 693 require.Nil(t, err) 694 err = b.MapSet(rowKey2, MapPair{ 695 Key: []byte("row2-key2"), 696 Value: []byte("row2-key2-reinserted"), 697 }) 698 require.Nil(t, err) 699 }) 700 701 t.Run("orderly shutdown", func(t *testing.T) { 702 b.Shutdown(context.Background()) 703 }) 704 705 t.Run("init another bucket on the same files", func(t *testing.T) { 706 b2, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 707 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 708 require.Nil(t, err) 709 710 row1Updated := []MapPair{ 711 // key 1 was deleted 712 { 713 Key: []byte("row1-key2"), 714 Value: []byte("row1-key2-value1"), 715 }, 716 } 717 718 row2Updated := []MapPair{ 719 { 720 Key: []byte("row2-key1"), 721 Value: []byte("row2-key1-value1"), 722 }, { 723 Key: []byte("row2-key2"), 724 Value: []byte("row2-key2-reinserted"), 725 }, 726 } 727 728 // NOTE: We are accepting that the order is changed here. Given the name 729 // "MapCollection" there should be no expectations regarding the order, 730 // but we have yet to validate if this fits with all of the intended use 731 // cases. 732 res, err := b2.MapList(rowKey1) 733 require.Nil(t, err) 734 assert.ElementsMatch(t, row1Updated, res) 735 res, err = b2.MapList(rowKey2) 736 require.Nil(t, err) 737 assert.ElementsMatch(t, row2Updated, res) 738 }) 739 }) 740 } 741 742 func mapCursors(ctx context.Context, t *testing.T, opts []BucketOption) { 743 t.Run("memtable-only", func(t *testing.T) { 744 r := getRandomSeed() 745 dirName := t.TempDir() 746 747 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 748 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 749 require.Nil(t, err) 750 751 // so big it effectively never triggers as part of this test 752 b.SetMemtableThreshold(1e9) 753 754 t.Run("set original values", func(t *testing.T) { 755 pairs := 20 756 valuesPerPair := 3 757 keys := make([][]byte, pairs) 758 values := make([][]MapPair, pairs) 759 760 for i := range keys { 761 keys[i] = []byte(fmt.Sprintf("row-%03d", i)) 762 values[i] = make([]MapPair, valuesPerPair) 763 for j := range values[i] { 764 values[i][j] = MapPair{ 765 Key: []byte(fmt.Sprintf("row-%03d-key-%d", i, j)), 766 Value: []byte(fmt.Sprintf("row-%03d-value-%d", i, j)), 767 } 768 } 769 } 770 771 // shuffle to make sure the BST isn't accidentally in order 772 r.Shuffle(len(keys), func(i, j int) { 773 keys[i], keys[j] = keys[j], keys[i] 774 values[i], values[j] = values[j], values[i] 775 }) 776 777 for i := range keys { 778 mapPairs := values[i] 779 for j := range mapPairs { 780 err = b.MapSet(keys[i], mapPairs[j]) 781 require.Nil(t, err) 782 } 783 } 784 }) 785 786 t.Run("seek from somewhere in the middle", func(t *testing.T) { 787 expectedKeys := [][]byte{ 788 []byte("row-016"), 789 []byte("row-017"), 790 []byte("row-018"), 791 []byte("row-019"), 792 } 793 expectedValues := [][]MapPair{ 794 { 795 {Key: []byte("row-016-key-0"), Value: []byte("row-016-value-0")}, 796 {Key: []byte("row-016-key-1"), Value: []byte("row-016-value-1")}, 797 {Key: []byte("row-016-key-2"), Value: []byte("row-016-value-2")}, 798 }, 799 { 800 {Key: []byte("row-017-key-0"), Value: []byte("row-017-value-0")}, 801 {Key: []byte("row-017-key-1"), Value: []byte("row-017-value-1")}, 802 {Key: []byte("row-017-key-2"), Value: []byte("row-017-value-2")}, 803 }, 804 { 805 {Key: []byte("row-018-key-0"), Value: []byte("row-018-value-0")}, 806 {Key: []byte("row-018-key-1"), Value: []byte("row-018-value-1")}, 807 {Key: []byte("row-018-key-2"), Value: []byte("row-018-value-2")}, 808 }, 809 { 810 {Key: []byte("row-019-key-0"), Value: []byte("row-019-value-0")}, 811 {Key: []byte("row-019-key-1"), Value: []byte("row-019-value-1")}, 812 {Key: []byte("row-019-key-2"), Value: []byte("row-019-value-2")}, 813 }, 814 } 815 816 var retrievedKeys [][]byte 817 var retrievedValues [][]MapPair 818 c := b.MapCursor() 819 defer c.Close() 820 for k, v := c.Seek([]byte("row-016")); k != nil; k, v = c.Next() { 821 retrievedKeys = append(retrievedKeys, k) 822 retrievedValues = append(retrievedValues, v) 823 } 824 825 assert.Equal(t, expectedKeys, retrievedKeys) 826 827 require.Equal(t, len(expectedValues), len(retrievedValues)) 828 for i := range expectedValues { 829 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 830 } 831 }) 832 833 t.Run("start from beginning", func(t *testing.T) { 834 expectedKeys := [][]byte{ 835 []byte("row-000"), 836 []byte("row-001"), 837 []byte("row-002"), 838 } 839 expectedValues := [][]MapPair{ 840 { 841 {Key: []byte("row-000-key-0"), Value: []byte("row-000-value-0")}, 842 {Key: []byte("row-000-key-1"), Value: []byte("row-000-value-1")}, 843 {Key: []byte("row-000-key-2"), Value: []byte("row-000-value-2")}, 844 }, 845 { 846 {Key: []byte("row-001-key-0"), Value: []byte("row-001-value-0")}, 847 {Key: []byte("row-001-key-1"), Value: []byte("row-001-value-1")}, 848 {Key: []byte("row-001-key-2"), Value: []byte("row-001-value-2")}, 849 }, 850 { 851 {Key: []byte("row-002-key-0"), Value: []byte("row-002-value-0")}, 852 {Key: []byte("row-002-key-1"), Value: []byte("row-002-value-1")}, 853 {Key: []byte("row-002-key-2"), Value: []byte("row-002-value-2")}, 854 }, 855 } 856 857 var retrievedKeys [][]byte 858 var retrievedValues [][]MapPair 859 c := b.MapCursor() 860 defer c.Close() 861 retrieved := 0 862 for k, v := c.First(); k != nil && retrieved < 3; k, v = c.Next() { 863 retrieved++ 864 retrievedKeys = append(retrievedKeys, k) 865 retrievedValues = append(retrievedValues, v) 866 } 867 868 assert.Equal(t, expectedKeys, retrievedKeys) 869 870 require.Equal(t, len(expectedValues), len(retrievedValues)) 871 for i := range expectedValues { 872 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 873 } 874 }) 875 876 t.Run("delete/replace an existing map key/value pair", func(t *testing.T) { 877 row := []byte("row-002") 878 pair := MapPair{ 879 Key: []byte("row-002-key-1"), // existing key 880 Value: []byte("row-002-value-1-updated"), // updated value 881 } 882 883 require.Nil(t, b.MapSet(row, pair)) 884 885 row = []byte("row-001") 886 key := []byte("row-001-key-1") 887 888 require.Nil(t, b.MapDeleteKey(row, key)) 889 }) 890 891 t.Run("verify update is contained", func(t *testing.T) { 892 expectedKeys := [][]byte{ 893 []byte("row-001"), 894 []byte("row-002"), 895 } 896 expectedValues := [][]MapPair{ 897 { 898 {Key: []byte("row-001-key-0"), Value: []byte("row-001-value-0")}, 899 // key-1 was deleted 900 {Key: []byte("row-001-key-2"), Value: []byte("row-001-value-2")}, 901 }, 902 { 903 {Key: []byte("row-002-key-0"), Value: []byte("row-002-value-0")}, 904 {Key: []byte("row-002-key-1"), Value: []byte("row-002-value-1-updated")}, 905 {Key: []byte("row-002-key-2"), Value: []byte("row-002-value-2")}, 906 }, 907 } 908 909 var retrievedKeys [][]byte 910 var retrievedValues [][]MapPair 911 c := b.MapCursor() 912 defer c.Close() 913 retrieved := 0 914 for k, v := c.Seek([]byte("row-001")); k != nil && retrieved < 2; k, v = c.Next() { 915 retrieved++ 916 retrievedKeys = append(retrievedKeys, k) 917 retrievedValues = append(retrievedValues, v) 918 } 919 920 assert.Equal(t, expectedKeys, retrievedKeys) 921 922 require.Equal(t, len(expectedValues), len(retrievedValues)) 923 for i := range expectedValues { 924 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 925 } 926 }) 927 }) 928 929 t.Run("with flushes", func(t *testing.T) { 930 r := getRandomSeed() 931 dirName := t.TempDir() 932 933 b, err := NewBucket(ctx, dirName, "", nullLogger(), nil, 934 cyclemanager.NewCallbackGroupNoop(), cyclemanager.NewCallbackGroupNoop(), opts...) 935 require.Nil(t, err) 936 937 // so big it effectively never triggers as part of this test 938 b.SetMemtableThreshold(1e9) 939 940 t.Run("first third (%3==0)", func(t *testing.T) { 941 pairs := 20 942 valuesPerPair := 3 943 var keys [][]byte 944 var values [][]MapPair 945 946 for i := 0; i < pairs; i++ { 947 if i%3 != 0 { 948 continue 949 } 950 951 keys = append(keys, []byte(fmt.Sprintf("row-%03d", i))) 952 curValues := make([]MapPair, valuesPerPair) 953 for j := range curValues { 954 curValues[j] = MapPair{ 955 Key: []byte(fmt.Sprintf("row-%03d-key-%d", i, j)), 956 Value: []byte(fmt.Sprintf("row-%03d-value-%d", i, j)), 957 } 958 } 959 960 values = append(values, curValues) 961 } 962 963 // shuffle to make sure the BST isn't accidentally in order 964 r.Shuffle(len(keys), func(i, j int) { 965 keys[i], keys[j] = keys[j], keys[i] 966 values[i], values[j] = values[j], values[i] 967 }) 968 969 for i := range keys { 970 mapPairs := values[i] 971 for j := range mapPairs { 972 err = b.MapSet(keys[i], mapPairs[j]) 973 require.Nil(t, err) 974 } 975 } 976 }) 977 978 t.Run("flush to disk", func(t *testing.T) { 979 require.Nil(t, b.FlushAndSwitch()) 980 }) 981 982 t.Run("second third (%3==1)", func(t *testing.T) { 983 pairs := 20 984 valuesPerPair := 3 985 var keys [][]byte 986 var values [][]MapPair 987 988 for i := 0; i < pairs; i++ { 989 if i%3 != 1 { 990 continue 991 } 992 993 keys = append(keys, []byte(fmt.Sprintf("row-%03d", i))) 994 curValues := make([]MapPair, valuesPerPair) 995 for j := range curValues { 996 curValues[j] = MapPair{ 997 Key: []byte(fmt.Sprintf("row-%03d-key-%d", i, j)), 998 Value: []byte(fmt.Sprintf("row-%03d-value-%d", i, j)), 999 } 1000 } 1001 1002 values = append(values, curValues) 1003 } 1004 1005 // shuffle to make sure the BST isn't accidentally in order 1006 r.Shuffle(len(keys), func(i, j int) { 1007 keys[i], keys[j] = keys[j], keys[i] 1008 values[i], values[j] = values[j], values[i] 1009 }) 1010 1011 for i := range keys { 1012 mapPairs := values[i] 1013 for j := range mapPairs { 1014 err = b.MapSet(keys[i], mapPairs[j]) 1015 require.Nil(t, err) 1016 } 1017 } 1018 }) 1019 1020 t.Run("flush to disk", func(t *testing.T) { 1021 require.Nil(t, b.FlushAndSwitch()) 1022 }) 1023 1024 t.Run("third third (%3==2) memtable only", func(t *testing.T) { 1025 pairs := 20 1026 valuesPerPair := 3 1027 var keys [][]byte 1028 var values [][]MapPair 1029 1030 for i := 0; i < pairs; i++ { 1031 if i%3 != 2 { 1032 continue 1033 } 1034 1035 keys = append(keys, []byte(fmt.Sprintf("row-%03d", i))) 1036 curValues := make([]MapPair, valuesPerPair) 1037 for j := range curValues { 1038 curValues[j] = MapPair{ 1039 Key: []byte(fmt.Sprintf("row-%03d-key-%d", i, j)), 1040 Value: []byte(fmt.Sprintf("row-%03d-value-%d", i, j)), 1041 } 1042 } 1043 1044 values = append(values, curValues) 1045 } 1046 1047 // shuffle to make sure the BST isn't accidentally in order 1048 r.Shuffle(len(keys), func(i, j int) { 1049 keys[i], keys[j] = keys[j], keys[i] 1050 values[i], values[j] = values[j], values[i] 1051 }) 1052 1053 for i := range keys { 1054 mapPairs := values[i] 1055 for j := range mapPairs { 1056 err = b.MapSet(keys[i], mapPairs[j]) 1057 require.Nil(t, err) 1058 } 1059 } 1060 1061 // no flush for this one, so this segment stays in the memtable 1062 }) 1063 1064 t.Run("seek from somewhere in the middle", func(t *testing.T) { 1065 expectedKeys := [][]byte{ 1066 []byte("row-016"), 1067 []byte("row-017"), 1068 []byte("row-018"), 1069 []byte("row-019"), 1070 } 1071 expectedValues := [][]MapPair{ 1072 { 1073 {Key: []byte("row-016-key-0"), Value: []byte("row-016-value-0")}, 1074 {Key: []byte("row-016-key-1"), Value: []byte("row-016-value-1")}, 1075 {Key: []byte("row-016-key-2"), Value: []byte("row-016-value-2")}, 1076 }, 1077 { 1078 {Key: []byte("row-017-key-0"), Value: []byte("row-017-value-0")}, 1079 {Key: []byte("row-017-key-1"), Value: []byte("row-017-value-1")}, 1080 {Key: []byte("row-017-key-2"), Value: []byte("row-017-value-2")}, 1081 }, 1082 { 1083 {Key: []byte("row-018-key-0"), Value: []byte("row-018-value-0")}, 1084 {Key: []byte("row-018-key-1"), Value: []byte("row-018-value-1")}, 1085 {Key: []byte("row-018-key-2"), Value: []byte("row-018-value-2")}, 1086 }, 1087 { 1088 {Key: []byte("row-019-key-0"), Value: []byte("row-019-value-0")}, 1089 {Key: []byte("row-019-key-1"), Value: []byte("row-019-value-1")}, 1090 {Key: []byte("row-019-key-2"), Value: []byte("row-019-value-2")}, 1091 }, 1092 } 1093 1094 var retrievedKeys [][]byte 1095 var retrievedValues [][]MapPair 1096 c := b.MapCursor() 1097 defer c.Close() 1098 for k, v := c.Seek([]byte("row-016")); k != nil; k, v = c.Next() { 1099 retrievedKeys = append(retrievedKeys, k) 1100 retrievedValues = append(retrievedValues, v) 1101 } 1102 1103 assert.Equal(t, expectedKeys, retrievedKeys) 1104 1105 require.Equal(t, len(expectedValues), len(retrievedValues)) 1106 for i := range expectedValues { 1107 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 1108 } 1109 }) 1110 1111 t.Run("start from beginning", func(t *testing.T) { 1112 expectedKeys := [][]byte{ 1113 []byte("row-000"), 1114 []byte("row-001"), 1115 []byte("row-002"), 1116 } 1117 expectedValues := [][]MapPair{ 1118 { 1119 {Key: []byte("row-000-key-0"), Value: []byte("row-000-value-0")}, 1120 {Key: []byte("row-000-key-1"), Value: []byte("row-000-value-1")}, 1121 {Key: []byte("row-000-key-2"), Value: []byte("row-000-value-2")}, 1122 }, 1123 { 1124 {Key: []byte("row-001-key-0"), Value: []byte("row-001-value-0")}, 1125 {Key: []byte("row-001-key-1"), Value: []byte("row-001-value-1")}, 1126 {Key: []byte("row-001-key-2"), Value: []byte("row-001-value-2")}, 1127 }, 1128 { 1129 {Key: []byte("row-002-key-0"), Value: []byte("row-002-value-0")}, 1130 {Key: []byte("row-002-key-1"), Value: []byte("row-002-value-1")}, 1131 {Key: []byte("row-002-key-2"), Value: []byte("row-002-value-2")}, 1132 }, 1133 } 1134 1135 var retrievedKeys [][]byte 1136 var retrievedValues [][]MapPair 1137 c := b.MapCursor() 1138 defer c.Close() 1139 retrieved := 0 1140 for k, v := c.First(); k != nil && retrieved < 3; k, v = c.Next() { 1141 retrieved++ 1142 retrievedKeys = append(retrievedKeys, k) 1143 retrievedValues = append(retrievedValues, v) 1144 } 1145 1146 assert.Equal(t, expectedKeys, retrievedKeys) 1147 1148 require.Equal(t, len(expectedValues), len(retrievedValues)) 1149 for i := range expectedValues { 1150 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 1151 } 1152 }) 1153 1154 t.Run("delete/replace an existing map key/value pair", func(t *testing.T) { 1155 row := []byte("row-002") 1156 pair := MapPair{ 1157 Key: []byte("row-002-key-1"), // existing key 1158 Value: []byte("row-002-value-1-updated"), // updated value 1159 } 1160 1161 require.Nil(t, b.MapSet(row, pair)) 1162 1163 row = []byte("row-001") 1164 key := []byte("row-001-key-1") 1165 1166 require.Nil(t, b.MapDeleteKey(row, key)) 1167 }) 1168 1169 t.Run("verify update is contained", func(t *testing.T) { 1170 expectedKeys := [][]byte{ 1171 []byte("row-001"), 1172 []byte("row-002"), 1173 } 1174 expectedValues := [][]MapPair{ 1175 { 1176 {Key: []byte("row-001-key-0"), Value: []byte("row-001-value-0")}, 1177 // key-1 was deleted 1178 {Key: []byte("row-001-key-2"), Value: []byte("row-001-value-2")}, 1179 }, 1180 { 1181 {Key: []byte("row-002-key-0"), Value: []byte("row-002-value-0")}, 1182 {Key: []byte("row-002-key-1"), Value: []byte("row-002-value-1-updated")}, 1183 {Key: []byte("row-002-key-2"), Value: []byte("row-002-value-2")}, 1184 }, 1185 } 1186 1187 var retrievedKeys [][]byte 1188 var retrievedValues [][]MapPair 1189 c := b.MapCursor() 1190 defer c.Close() 1191 retrieved := 0 1192 for k, v := c.Seek([]byte("row-001")); k != nil && retrieved < 2; k, v = c.Next() { 1193 retrieved++ 1194 retrievedKeys = append(retrievedKeys, k) 1195 retrievedValues = append(retrievedValues, v) 1196 } 1197 1198 assert.Equal(t, expectedKeys, retrievedKeys) 1199 1200 require.Equal(t, len(expectedValues), len(retrievedValues)) 1201 for i := range expectedValues { 1202 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 1203 } 1204 }) 1205 1206 t.Run("one final flush to disk", func(t *testing.T) { 1207 require.Nil(t, b.FlushAndSwitch()) 1208 }) 1209 1210 t.Run("verify update is contained - after flushing the update", func(t *testing.T) { 1211 expectedKeys := [][]byte{ 1212 []byte("row-001"), 1213 []byte("row-002"), 1214 } 1215 expectedValues := [][]MapPair{ 1216 { 1217 {Key: []byte("row-001-key-0"), Value: []byte("row-001-value-0")}, 1218 // key-1 was deleted 1219 {Key: []byte("row-001-key-2"), Value: []byte("row-001-value-2")}, 1220 }, 1221 { 1222 {Key: []byte("row-002-key-0"), Value: []byte("row-002-value-0")}, 1223 {Key: []byte("row-002-key-1"), Value: []byte("row-002-value-1-updated")}, 1224 {Key: []byte("row-002-key-2"), Value: []byte("row-002-value-2")}, 1225 }, 1226 } 1227 1228 var retrievedKeys [][]byte 1229 var retrievedValues [][]MapPair 1230 c := b.MapCursor() 1231 defer c.Close() 1232 retrieved := 0 1233 for k, v := c.Seek([]byte("row-001")); k != nil && retrieved < 2; k, v = c.Next() { 1234 retrieved++ 1235 retrievedKeys = append(retrievedKeys, k) 1236 retrievedValues = append(retrievedValues, v) 1237 } 1238 1239 assert.Equal(t, expectedKeys, retrievedKeys) 1240 1241 require.Equal(t, len(expectedValues), len(retrievedValues)) 1242 for i := range expectedValues { 1243 assert.ElementsMatch(t, expectedValues[i], retrievedValues[i]) 1244 } 1245 }) 1246 }) 1247 }