github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/metacache_test.go (about) 1 // Copyright (C) 2015 The GoHBase Authors. All rights reserved. 2 // This file is part of GoHBase. 3 // Use of this source code is governed by the Apache License 2.0 4 // that can be found in the COPYING file. 5 6 package gohbase 7 8 import ( 9 "bytes" 10 "fmt" 11 "reflect" 12 "sort" 13 "strconv" 14 "testing" 15 16 "github.com/tsuna/gohbase/hrpc" 17 "github.com/tsuna/gohbase/region" 18 mockRegion "github.com/tsuna/gohbase/test/mock/region" 19 "go.uber.org/mock/gomock" 20 ) 21 22 func TestMetaCache(t *testing.T) { 23 client := newClient("~invalid.quorum~") // We shouldn't connect to ZK. 24 25 reg := client.getRegionFromCache([]byte("test"), []byte("theKey")) 26 if reg != nil { 27 t.Errorf("Found region %v even though the cache was empty?!", reg) 28 } 29 30 // Inject an entry in the cache. This entry covers the entire key range. 31 wholeTable := region.NewInfo( 32 0, 33 nil, 34 []byte("test"), 35 []byte("test,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 36 nil, 37 nil, 38 ) 39 ctrl := gomock.NewController(t) 40 defer ctrl.Finish() 41 42 regClient := mockRegion.NewMockRegionClient(ctrl) 43 regClient.EXPECT().Addr().Return("regionserver:1").AnyTimes() 44 regClient.EXPECT().String().Return("mock region client").AnyTimes() 45 newClientFn := func() hrpc.RegionClient { 46 return regClient 47 } 48 49 client.regions.put(wholeTable) 50 client.clients.put("regionserver:1", wholeTable, newClientFn) 51 52 reg = client.getRegionFromCache([]byte("test"), []byte("theKey")) 53 if !reflect.DeepEqual(reg, wholeTable) { 54 t.Errorf("Found region %v but expected %v", reg, wholeTable) 55 } 56 reg = client.getRegionFromCache([]byte("test"), []byte("")) // edge case. 57 if !reflect.DeepEqual(reg, wholeTable) { 58 t.Errorf("Found region %v but expected %v", reg, wholeTable) 59 } 60 61 // Clear our client. 62 client = newClient("~invalid.quorum~") 63 64 // Inject 3 entries in the cache. 65 region1 := region.NewInfo( 66 0, 67 nil, 68 []byte("test"), 69 []byte("test,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 70 []byte(""), 71 []byte("foo"), 72 ) 73 if os, replaced := client.regions.put(region1); !replaced { 74 t.Errorf("Expected to put new region into cache, got: %v", os) 75 } else if len(os) != 0 { 76 t.Errorf("Didn't expect any overlaps, got: %v", os) 77 } 78 client.clients.put("regionserver:1", region1, newClientFn) 79 80 region2 := region.NewInfo( 81 0, 82 nil, 83 []byte("test"), 84 []byte("test,foo,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 85 []byte("foo"), 86 []byte("gohbase"), 87 ) 88 if os, replaced := client.regions.put(region2); !replaced { 89 t.Errorf("Expected to put new region into cache, got: %v", os) 90 } else if len(os) != 0 { 91 t.Errorf("Didn't expect any overlaps, got: %v", os) 92 } 93 client.clients.put("regionserver:1", region2, newClientFn) 94 95 region3 := region.NewInfo( 96 0, 97 nil, 98 []byte("test"), 99 []byte("test,gohbase,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 100 []byte("gohbase"), 101 []byte(""), 102 ) 103 if os, replaced := client.regions.put(region3); !replaced { 104 t.Errorf("Expected to put new region into cache, got: %v", os) 105 } else if len(os) != 0 { 106 t.Errorf("Didn't expect any overlaps, got: %v", os) 107 } 108 client.clients.put("regionserver:1", region3, newClientFn) 109 110 testcases := []struct { 111 key string 112 reg hrpc.RegionInfo 113 }{ 114 {key: "theKey", reg: region3}, 115 {key: "", reg: region1}, 116 {key: "bar", reg: region1}, 117 {key: "fon\xFF", reg: region1}, 118 {key: "foo", reg: region2}, 119 {key: "foo\x00", reg: region2}, 120 {key: "gohbase", reg: region3}, 121 } 122 for i, testcase := range testcases { 123 reg = client.getRegionFromCache([]byte("test"), []byte(testcase.key)) 124 if !reflect.DeepEqual(reg, testcase.reg) { 125 t.Errorf("[#%d] Found region %v but expected %v", i, reg, testcase.reg) 126 } 127 } 128 129 // Change the last region (maybe it got split). 130 region4 := region.NewInfo( 131 0, 132 nil, 133 []byte("test"), 134 []byte("test,gohbase,1234567890042.swagswagswagswagswagswagswagswag."), 135 []byte("gohbase"), 136 []byte("zab"), 137 ) 138 if os, replaced := client.regions.put(region4); !replaced { 139 t.Errorf("Expected to put new region into cache, got: %v", os) 140 } else if len(os) != 1 || os[0] != region3 { 141 t.Errorf("Expected one overlap, got: %v", os) 142 } 143 client.clients.put("regionserver:1", region4, newClientFn) 144 145 reg = client.getRegionFromCache([]byte("test"), []byte("theKey")) 146 if !reflect.DeepEqual(reg, region4) { 147 t.Errorf("Found region %v but expected %v", reg, region4) 148 } 149 reg = client.getRegionFromCache([]byte("test"), []byte("zoo")) 150 if reg != nil { 151 t.Errorf("Shouldn't have found any region yet found %v", reg) 152 } 153 154 // attempt putting a region with same name 155 region5 := region.NewInfo( 156 0, 157 nil, 158 []byte("test"), 159 []byte("test,gohbase,1234567890042.swagswagswagswagswagswagswagswag."), 160 nil, 161 []byte("zab"), 162 ) 163 if os, replaced := client.regions.put(region5); replaced { 164 t.Errorf("Expected to not replace a region in cache, got: %v", os) 165 } else if len(os) != 1 || os[0] != region4 { 166 t.Errorf("Expected overlaps, got: %v", os) 167 } 168 } 169 170 func TestMetaCacheGet(t *testing.T) { 171 tcases := []struct { 172 in []hrpc.RegionInfo 173 table []byte 174 key []byte 175 outIndexFromIn int 176 }{ 177 { 178 table: []byte("yolo"), 179 key: []byte("swag"), 180 outIndexFromIn: -1, 181 }, 182 { // the whole table 183 in: []hrpc.RegionInfo{ 184 region.NewInfo(0, nil, []byte("test"), 185 []byte("test,,1234567890042.swagswagswagswagswagswagswagswag."), 186 nil, nil), 187 }, 188 table: []byte("test"), 189 key: []byte("swag"), 190 outIndexFromIn: 0, 191 }, 192 { // one region in cache, different table 193 in: []hrpc.RegionInfo{ 194 region.NewInfo(0, nil, []byte("test"), 195 []byte("test,,1234567890042.swagswagswagswagswagswagswagswag."), 196 nil, nil), 197 }, 198 table: []byte("yolo"), 199 key: []byte("swag"), 200 outIndexFromIn: -1, 201 }, 202 { // key is before the first region in cache 203 in: []hrpc.RegionInfo{ 204 region.NewInfo(0, nil, []byte("test"), 205 []byte("test,foo,1234567890042.swagswagswagswagswagswagswagswag."), 206 []byte("foo"), nil), 207 }, 208 table: []byte("test"), 209 key: []byte("bar"), 210 outIndexFromIn: -1, 211 }, 212 { // key is between two regions in cache 213 in: []hrpc.RegionInfo{ 214 region.NewInfo(0, nil, []byte("meow"), 215 []byte("meow,bar,1234567890042.swagswagswagswagswagswagswagswag."), 216 []byte("bar"), []byte("foo")), 217 region.NewInfo(0, nil, []byte("test"), 218 []byte("test,foo,1234567890042.swagswagswagswagswagswagswagswag."), 219 []byte("foo"), nil), 220 }, 221 table: []byte("meow"), 222 key: []byte("swag"), 223 outIndexFromIn: -1, 224 }, 225 { // test with namespace region in cache 226 in: []hrpc.RegionInfo{ 227 region.NewInfo(0, []byte("n1"), []byte("test"), 228 []byte("n1:test,,1234567890042.swagswagswagswagswagswagswagswag."), 229 nil, nil), 230 }, 231 table: []byte("test"), 232 key: []byte("swag"), 233 outIndexFromIn: -1, 234 }, 235 { // test with namespace region in cache 236 in: []hrpc.RegionInfo{ 237 region.NewInfo(0, []byte("n1"), []byte("test"), 238 []byte("n1:test,,1234567890042.swagswagswagswagswagswagswagswag."), 239 nil, nil), 240 }, 241 table: []byte("n1:test"), 242 key: []byte("swag"), 243 outIndexFromIn: 0, 244 }, 245 { // test with default namespace in cache, but non-default key 246 in: []hrpc.RegionInfo{ 247 region.NewInfo(0, nil, []byte("test"), 248 []byte("test,,1234567890042.swagswagswagswagswagswagswagswag."), 249 nil, nil), 250 }, 251 table: []byte("n1:test"), 252 key: []byte("swag"), 253 outIndexFromIn: -1, 254 }, 255 { // test with non-default namespace region in cache, but default key 256 in: []hrpc.RegionInfo{ 257 region.NewInfo(0, []byte("n1"), []byte("test"), 258 []byte("n1:test,,1234567890042.swagswagswagswagswagswagswagswag."), 259 nil, nil), 260 }, 261 table: []byte("test"), 262 key: []byte("swag"), 263 outIndexFromIn: -1, 264 }, 265 { // test 3 regions 266 in: []hrpc.RegionInfo{ 267 region.NewInfo(0, nil, []byte("test"), 268 []byte("test,,1234567890042.swagswagswagswagswagswagswagswag."), 269 nil, []byte("bar")), 270 region.NewInfo(0, nil, []byte("test"), 271 []byte("test,bar,1234567890042.swagswagswagswagswagswagswagswag."), 272 []byte("bar"), []byte("foo")), 273 region.NewInfo(0, nil, []byte("test"), 274 []byte("test,foo,1234567890042.swagswagswagswagswagswagswagswag."), 275 []byte("foo"), []byte("yolo")), 276 }, 277 table: []byte("test"), 278 key: []byte("baz"), 279 outIndexFromIn: 1, 280 }, 281 } 282 283 for i, tcase := range tcases { 284 t.Run(fmt.Sprintf("Test %d", i), func(t *testing.T) { 285 client := newClient("~invalid.quorum~") // We shouldn't connect to ZK. 286 287 for _, r := range tcase.in { 288 overlaps, replaced := client.regions.put(r) 289 if len(overlaps) != 0 { 290 t.Fatalf("Didn't expect any overlaps, got %q", overlaps) 291 } 292 if !replaced { 293 t.Fatal("Didn't expect to replace anything in cache") 294 } 295 } 296 297 // lookup region in cache 298 region := client.getRegionFromCache(tcase.table, tcase.key) 299 300 if tcase.outIndexFromIn == -1 && region != nil { 301 t.Fatalf("expected to get nil region, got %v", region) 302 } else { 303 return 304 } 305 306 if len(tcase.in) == 0 && region != nil { 307 t.Fatalf("didn't expect to get anything from empty cache, got %v", region) 308 } 309 310 if tcase.in[tcase.outIndexFromIn].String() != region.String() { 311 t.Errorf("Expected %v, Got %v", 312 tcase.in[tcase.outIndexFromIn].String(), region.String()) 313 } 314 }) 315 } 316 } 317 318 func TestRegionCacheAge(t *testing.T) { 319 tcases := []struct { 320 cachedRegions []hrpc.RegionInfo 321 newRegion hrpc.RegionInfo 322 replaced bool 323 }{ 324 { // all older 325 cachedRegions: []hrpc.RegionInfo{ 326 region.NewInfo( 327 1, nil, []byte("hello"), 328 []byte("hello,,1.yoloyoloyoloyoloyoloyoloyoloyolo."), 329 []byte(""), []byte("foo"), 330 ), 331 region.NewInfo( 332 1, nil, []byte("hello"), 333 []byte("hello,foo,1.swagswagswagswagswagswagswagswag."), 334 []byte("foo"), []byte(""), 335 )}, 336 newRegion: region.NewInfo( 337 2, nil, []byte("hello"), 338 []byte("hello,,2.meowmemowmeowmemowmeowmemowmeow."), 339 []byte(""), []byte(""), 340 ), 341 replaced: true, 342 }, 343 { // all younger 344 cachedRegions: []hrpc.RegionInfo{ 345 region.NewInfo( 346 2, nil, []byte("hello"), 347 []byte("hello,,2.yoloyoloyoloyoloyoloyoloyoloyolo."), 348 []byte(""), []byte("foo"), 349 ), 350 region.NewInfo( 351 2, nil, []byte("hello"), 352 []byte("hello,foo,2.swagswagswagswagswagswagswagswag."), 353 []byte("foo"), []byte(""), 354 )}, 355 newRegion: region.NewInfo( 356 1, nil, []byte("hello"), 357 []byte("hello,,1.meowmemowmeowmemowmeowmemowmeow."), 358 []byte(""), []byte(""), 359 ), 360 replaced: false, 361 }, 362 { // one younger, one older 363 cachedRegions: []hrpc.RegionInfo{ 364 region.NewInfo( 365 1, nil, []byte("hello"), 366 []byte("hello,,1.yoloyoloyoloyoloyoloyoloyoloyolo."), 367 []byte(""), []byte("foo"), 368 ), 369 region.NewInfo( 370 3, nil, []byte("hello"), 371 []byte("hello,foo,3.swagswagswagswagswagswagswagswag."), 372 []byte("foo"), []byte(""), 373 )}, 374 newRegion: region.NewInfo( 375 2, nil, []byte("hello"), 376 []byte("hello,,1.meowmemowmeowmemowmeowmemowmeow."), 377 []byte(""), []byte(""), 378 ), 379 replaced: false, 380 }, 381 } 382 383 client := newClient("~invalid.quorum~") 384 for i, tcase := range tcases { 385 t.Run(strconv.Itoa(i), func(t *testing.T) { 386 client.regions.regions.Clear() 387 // set up initial cache 388 for _, region := range tcase.cachedRegions { 389 client.regions.put(region) 390 } 391 392 overlaps, replaced := client.regions.put(tcase.newRegion) 393 if replaced != tcase.replaced { 394 t.Errorf("expected %v, got %v", tcase.replaced, replaced) 395 } 396 397 expectedNames := make(regionNames, len(tcase.cachedRegions)) 398 for i, r := range tcase.cachedRegions { 399 expectedNames[i] = r.Name() 400 } 401 osNames := make(regionNames, len(overlaps)) 402 for i, o := range overlaps { 403 osNames[i] = o.Name() 404 } 405 406 // check overlaps are correct 407 if !reflect.DeepEqual(expectedNames, osNames) { 408 t.Errorf("expected %v, got %v", expectedNames, osNames) 409 } 410 }) 411 } 412 } 413 414 type regionNames [][]byte 415 416 func (a regionNames) Len() int { return len(a) } 417 func (a regionNames) Less(i, j int) bool { return bytes.Compare(a[i], a[j]) < 0 } 418 func (a regionNames) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 419 420 func TestMetaCacheGetOverlaps(t *testing.T) { 421 region1 := region.NewInfo( 422 0, 423 nil, 424 []byte("test"), 425 []byte("test,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 426 []byte(""), 427 []byte("foo"), 428 ) 429 430 regionA := region.NewInfo( 431 0, 432 nil, 433 []byte("hello"), 434 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 435 []byte(""), 436 []byte("foo"), 437 ) 438 439 regionB := region.NewInfo( 440 0, 441 nil, 442 []byte("hello"), 443 []byte("hello,foo,987654321042.56f833d5569a27c7a43fbf547b4924a4."), 444 []byte("foo"), 445 []byte("fox"), 446 ) 447 448 regionC := region.NewInfo( 449 0, 450 nil, 451 []byte("hello"), 452 []byte("hello,fox,987654321042.56f833d5569a27c7a43fbf547b4924a4."), 453 []byte("fox"), 454 []byte("yolo"), 455 ) 456 457 regionWhole := region.NewInfo( 458 0, 459 nil, 460 []byte("hello"), 461 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 462 nil, 463 nil, 464 ) 465 466 regionTests := []struct { 467 cachedRegions []hrpc.RegionInfo 468 newRegion hrpc.RegionInfo 469 expected []hrpc.RegionInfo 470 }{ 471 {[]hrpc.RegionInfo{}, region1, []hrpc.RegionInfo{}}, // empty cache 472 {[]hrpc.RegionInfo{region1}, region1, []hrpc.RegionInfo{region1}}, // with itself 473 { // different table 474 []hrpc.RegionInfo{region1}, 475 region.NewInfo( 476 0, 477 nil, 478 []byte("hello"), 479 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 480 []byte(""), 481 []byte("fake"), 482 ), 483 []hrpc.RegionInfo{}, 484 }, 485 { // different namespace 486 []hrpc.RegionInfo{ 487 region.NewInfo( 488 0, 489 []byte("ns1"), 490 []byte("test"), 491 []byte("ns1:test,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 492 []byte(""), 493 []byte("foo"), 494 ), 495 }, 496 region.NewInfo( 497 0, 498 nil, 499 []byte("test"), 500 []byte("test,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 501 []byte(""), 502 []byte("foo"), 503 ), 504 []hrpc.RegionInfo{}, 505 }, 506 { // overlaps with both 507 []hrpc.RegionInfo{regionA, regionB}, 508 region.NewInfo( 509 0, 510 nil, 511 []byte("hello"), 512 []byte("hello,bar,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 513 []byte("bar"), 514 []byte("fop"), 515 ), 516 []hrpc.RegionInfo{regionA, regionB}, 517 }, 518 { // overlaps with both, key start == old one 519 []hrpc.RegionInfo{regionA, regionB}, 520 region.NewInfo( 521 0, 522 nil, 523 []byte("hello"), 524 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 525 []byte(""), 526 []byte("yolo"), 527 ), 528 []hrpc.RegionInfo{regionA, regionB}, 529 }, 530 { // overlaps with second 531 []hrpc.RegionInfo{regionA, regionB}, 532 region.NewInfo( 533 0, 534 nil, 535 []byte("hello"), 536 []byte("hello,fop,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 537 []byte("fop"), 538 []byte("yolo"), 539 ), 540 []hrpc.RegionInfo{regionB}, 541 }, 542 { // overlaps with first, new key start == old one 543 []hrpc.RegionInfo{regionA, regionB}, 544 region.NewInfo( 545 0, 546 nil, 547 []byte("hello"), 548 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 549 []byte(""), 550 []byte("abc"), 551 ), 552 []hrpc.RegionInfo{regionA}, 553 }, 554 { // doesn't overlap, is between existing 555 []hrpc.RegionInfo{regionA, regionC}, 556 regionB, 557 []hrpc.RegionInfo{}, 558 }, 559 { // without bounds in cache, replaced by region with both bounds 560 []hrpc.RegionInfo{regionWhole}, 561 regionB, 562 []hrpc.RegionInfo{regionWhole}, 563 }, 564 { // without bounds in cache, replaced by the empty stop key only 565 []hrpc.RegionInfo{regionWhole}, 566 region.NewInfo( 567 0, 568 nil, 569 []byte("hello"), 570 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 571 []byte("yolo"), 572 nil, 573 ), 574 []hrpc.RegionInfo{regionWhole}, 575 }, 576 { // without bounds in cache, replaced by the empty start key only 577 []hrpc.RegionInfo{regionWhole}, 578 region.NewInfo( 579 0, 580 nil, 581 []byte("hello"), 582 []byte("hello,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 583 nil, 584 []byte("yolo"), 585 ), 586 []hrpc.RegionInfo{regionWhole}, 587 }, 588 { // regions with bounds in cache, replaced by without bounds 589 []hrpc.RegionInfo{regionB, regionC}, 590 regionWhole, 591 []hrpc.RegionInfo{regionB, regionC}, 592 }, 593 { // without bounds in cache, replaced by without bounds 594 []hrpc.RegionInfo{regionWhole}, 595 region.NewInfo( 596 0, 597 nil, 598 []byte("hello"), 599 []byte("hello,,1234567890042.yoloyoloyoloyoloyoloyoloyoloyolo."), 600 nil, 601 nil, 602 ), 603 []hrpc.RegionInfo{regionWhole}, 604 }, 605 } 606 607 for i, tt := range regionTests { 608 t.Run(fmt.Sprintf("Test %d", i), func(t *testing.T) { 609 client := newClient("~invalid.quorum~") // fake client 610 // set up initial cache 611 for _, region := range tt.cachedRegions { 612 client.regions.regions.Set(region.Name(), region) 613 } 614 615 expectedNames := make(regionNames, len(tt.expected)) 616 for i, r := range tt.expected { 617 expectedNames[i] = r.Name() 618 } 619 os := client.regions.getOverlaps(tt.newRegion) 620 osNames := make(regionNames, len(os)) 621 for i, o := range os { 622 osNames[i] = o.Name() 623 } 624 sort.Sort(expectedNames) 625 sort.Sort(osNames) 626 if !reflect.DeepEqual(expectedNames, osNames) { 627 t.Errorf("Expected overlaps %q, found %q", expectedNames, osNames) 628 } 629 }) 630 } 631 } 632 633 func TestClientCachePut(t *testing.T) { 634 client := newClient("~invalid.quorum~") 635 636 ctrl := gomock.NewController(t) 637 defer ctrl.Finish() 638 639 var newClientCalled bool 640 641 regClient := client.clients.put("regionserver:1", region.NewInfo(0, nil, []byte("test"), 642 []byte("test,,1234567890042.yoloyoloyoloyoloyoloyoloyoloyolo."), nil, nil), 643 func() hrpc.RegionClient { 644 newClientCalled = true 645 regClient := mockRegion.NewMockRegionClient(ctrl) 646 regClient.EXPECT().Addr().Return("regionserver:1").AnyTimes() 647 regClient.EXPECT().String().Return("mock region client").AnyTimes() 648 return regClient 649 }) 650 651 if !newClientCalled { 652 t.Fatal("expected newClient to be called") 653 } 654 655 if len(client.clients.regions) != 1 { 656 t.Errorf("Expected 1 client in cache, got %d", len(client.clients.regions)) 657 } 658 659 if len(client.clients.regions[regClient]) != 1 { 660 t.Errorf("Expected 1 region for client in cache, got %d", 661 len(client.clients.regions[regClient])) 662 } 663 664 // but put a different region for the same address 665 regClient2 := client.clients.put("regionserver:1", region.NewInfo(0, nil, []byte("yolo"), 666 []byte("yolo,,1234567890042.yoloyoloyoloyoloyoloyoloyoloyolo."), nil, nil), 667 func() hrpc.RegionClient { 668 t.Fatal("newClient should not be called") 669 return nil 670 }) 671 672 if regClient2 != regClient { 673 t.Fatalf("expected to get the same exact region client: %s vs %s", regClient2, regClient) 674 } 675 676 // nothing should have changed in clients cache 677 if len(client.clients.regions) != 1 { 678 t.Errorf("Expected 1 client in cache, got %d", len(client.clients.regions)) 679 } 680 681 if len(client.clients.regions[regClient]) != 2 { 682 t.Errorf("Expected 2 regions for client in cache, got %d", 683 len(client.clients.regions[regClient])) 684 } 685 }