github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/rpc_test.go (about) 1 // Copyright (C) 2016 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 "context" 11 "errors" 12 "fmt" 13 "log/slog" 14 "math/rand" 15 "net" 16 "reflect" 17 "strconv" 18 "strings" 19 "sync" 20 "testing" 21 "time" 22 23 "github.com/google/go-cmp/cmp" 24 25 "github.com/prometheus/client_golang/prometheus" 26 "github.com/tsuna/gohbase/compression" 27 "github.com/tsuna/gohbase/hrpc" 28 "github.com/tsuna/gohbase/pb" 29 "github.com/tsuna/gohbase/region" 30 "github.com/tsuna/gohbase/test" 31 "github.com/tsuna/gohbase/test/mock" 32 mockRegion "github.com/tsuna/gohbase/test/mock/region" 33 mockZk "github.com/tsuna/gohbase/test/mock/zk" 34 "github.com/tsuna/gohbase/zk" 35 "go.uber.org/mock/gomock" 36 "google.golang.org/protobuf/proto" 37 "google.golang.org/protobuf/types/known/wrapperspb" 38 "modernc.org/b/v2" 39 ) 40 41 func newRegionClientFn(addr string) func() hrpc.RegionClient { 42 return func() hrpc.RegionClient { 43 return newMockRegionClient(addr, region.RegionClient, 44 0, 0, "root", region.DefaultReadTimeout, nil, nil, slog.Default()) 45 } 46 } 47 48 func newMockClient(zkClient zk.Client) *client { 49 return &client{ 50 clientType: region.RegionClient, 51 regions: keyRegionCache{ 52 logger: slog.Default(), 53 regions: b.TreeNew[[]byte, hrpc.RegionInfo](region.Compare), 54 }, 55 clients: clientRegionCache{ 56 logger: slog.Default(), 57 regions: make(map[hrpc.RegionClient]map[hrpc.RegionInfo]struct{}), 58 }, 59 rpcQueueSize: defaultRPCQueueSize, 60 flushInterval: defaultFlushInterval, 61 metaRegionInfo: region.NewInfo(0, []byte("hbase"), []byte("meta"), 62 []byte("hbase:meta,,1"), nil, nil), 63 zkTimeout: defaultZkTimeout, 64 zkClient: zkClient, 65 regionLookupTimeout: region.DefaultLookupTimeout, 66 regionReadTimeout: region.DefaultReadTimeout, 67 newRegionClientFn: newMockRegionClient, 68 logger: slog.Default(), 69 } 70 } 71 72 func TestSendRPCSanity(t *testing.T) { 73 ctrl := test.NewController(t) 74 defer ctrl.Finish() 75 // we expect to ask zookeeper for where metaregion is 76 zkClient := mockZk.NewMockClient(ctrl) 77 zkClient.EXPECT().LocateResource(zk.Meta).Return("regionserver:1", nil).MinTimes(1) 78 c := newMockClient(zkClient) 79 80 // ask for "theKey" in table "test" 81 mockCall := mock.NewMockCall(ctrl) 82 mockCall.EXPECT().Context().Return(context.Background()).AnyTimes() 83 mockCall.EXPECT().Description().AnyTimes() 84 mockCall.EXPECT().Table().Return([]byte("test")).AnyTimes() 85 mockCall.EXPECT().Key().Return([]byte("theKey")).AnyTimes() 86 mockCall.EXPECT().SetRegion(gomock.Any()).AnyTimes() 87 result := make(chan hrpc.RPCResult, 1) 88 89 // pretend that response is successful 90 expMsg := &pb.ScanResponse{} 91 result <- hrpc.RPCResult{Msg: expMsg} 92 mockCall.EXPECT().ResultChan().Return(result).Times(1) 93 msg, err := c.SendRPC(mockCall) 94 if err != nil { 95 t.Fatal(err) 96 } 97 if !proto.Equal(expMsg, msg) { 98 t.Errorf("expected %v, got %v", expMsg, msg) 99 } 100 101 if len(c.clients.regions) != 2 { 102 t.Errorf("Expected 2 clients in cache, got %d", len(c.clients.regions)) 103 } 104 105 // addr -> region name 106 expClients := map[string]string{ 107 "regionserver:1": "hbase:meta,,1", 108 "regionserver:2": "test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4.", 109 } 110 111 // make sure those are the right clients 112 for c, rs := range c.clients.regions { 113 name, ok := expClients[c.Addr()] 114 if !ok { 115 t.Errorf("Got unexpected client %s in cache", c.Addr()) 116 continue 117 } 118 if len(rs) != 1 { 119 t.Errorf("Expected to have only 1 region in cache for client %s", c.Addr()) 120 continue 121 } 122 for r := range rs { 123 if string(r.Name()) != name { 124 t.Errorf("Unexpected name of region %q for client %s, expected %q", 125 r.Name(), c.Addr(), name) 126 } 127 // check bidirectional mapping, they have to be the same objects 128 rc := r.Client() 129 if c != rc { 130 t.Errorf("Invalid bidirectional mapping: forward=%s, backward=%s", 131 c.Addr(), rc.Addr()) 132 } 133 } 134 } 135 136 if c.regions.regions.Len() != 1 { 137 // expecting only one region because meta isn't in cache, it's hard-coded 138 t.Errorf("Expected 1 regions in cache, got %d", c.regions.regions.Len()) 139 } 140 } 141 142 func TestReestablishRegionSplit(t *testing.T) { 143 ctrl := test.NewController(t) 144 defer ctrl.Finish() 145 // we expect to ask zookeeper for where metaregion is 146 c := newMockClient(mockZk.NewMockClient(ctrl)) 147 148 // inject a fake regionserver client and fake region into cache 149 origlReg := region.NewInfo( 150 0, 151 nil, 152 []byte("test1"), 153 []byte("test1,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."), 154 nil, 155 nil, 156 ) 157 158 // pretend regionserver:1 has meta table 159 // "test1" is at the moment at regionserver:1 160 // marking unavailable to simulate error 161 origlReg.MarkUnavailable() 162 rc1 := c.clients.put("regionserver:1", origlReg, newRegionClientFn("regionserver:1")) 163 origlReg.SetClient(rc1) 164 c.regions.put(origlReg) 165 166 rc2 := c.clients.put("regionserver:1", c.metaRegionInfo, newRegionClientFn("regionserver:1")) 167 if rc1 != rc2 { 168 t.Fatal("expected to get the same region client") 169 } 170 c.metaRegionInfo.SetClient(rc2) 171 172 c.reestablishRegion(origlReg) 173 174 if len(c.clients.regions) != 1 { 175 t.Errorf("Expected 1 client in cache, got %d", len(c.clients.regions)) 176 } 177 178 expRegs := map[string]struct{}{ 179 "hbase:meta,,1": struct{}{}, 180 "test1,,1480547738107.825c5c7e480c76b73d6d2bad5d3f7bb8.": struct{}{}, 181 } 182 183 // make sure those are the right clients 184 for rc, rs := range c.clients.regions { 185 if rc.Addr() != "regionserver:1" { 186 t.Errorf("Got unexpected client %s in cache", rc.Addr()) 187 break 188 } 189 190 // check that we have correct regions in the client 191 gotRegs := map[string]struct{}{} 192 for r := range rs { 193 gotRegs[string(r.Name())] = struct{}{} 194 // check that regions have correct client 195 if r.Client() != rc1 { 196 t.Errorf("Invalid bidirectional mapping: forward=%s, backward=%s", 197 r.Client().Addr(), rc1.Addr()) 198 } 199 } 200 if !reflect.DeepEqual(expRegs, gotRegs) { 201 t.Errorf("expected %v, got %v", expRegs, gotRegs) 202 } 203 204 // check that we still have the same client that we injected 205 if rc != rc1 { 206 t.Errorf("Invalid bidirectional mapping: forward=%s, backward=%s", 207 rc.Addr(), rc1.Addr()) 208 } 209 } 210 211 if c.regions.regions.Len() != 1 { 212 // expecting only one region because meta isn't in cache, it's hard-coded 213 t.Errorf("Expected 1 regions in cache, got %d", c.regions.regions.Len()) 214 } 215 216 // check the we have correct region in regions cache 217 newRegIntf, ok := c.regions.regions.Get( 218 []byte("test1,,1480547738107.825c5c7e480c76b73d6d2bad5d3f7bb8.")) 219 if !ok { 220 t.Error("Expected region is not in the cache") 221 } 222 223 // check new region is available 224 newReg, ok := newRegIntf.(hrpc.RegionInfo) 225 if !ok { 226 t.Error("Expected hrpc.RegionInfo") 227 } 228 if newReg.IsUnavailable() { 229 t.Error("Expected new region to be available") 230 } 231 232 // check old region is available and it's client is empty since we 233 // need to release all the waiting callers 234 if origlReg.IsUnavailable() { 235 t.Error("Expected original region to be available") 236 } 237 238 if origlReg.Client() != nil { 239 t.Error("Expected original region to have no client") 240 } 241 } 242 243 func TestReestablishRegionNSRE(t *testing.T) { 244 c := newMockClient(nil) 245 origlReg := region.NewInfo(0, nil, []byte("nsre"), 246 []byte("nsre,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."), nil, nil) 247 // inject a fake regionserver client and fake region into cache 248 // pretend regionserver:1 has meta table 249 rc1 := c.clients.put("regionserver:1", c.metaRegionInfo, newRegionClientFn("regionserver:1")) 250 rc2 := c.clients.put("regionserver:1", origlReg, newRegionClientFn("regionserver:1")) 251 if rc1 != rc2 { 252 t.Fatal("expected region client to be the same") 253 } 254 255 // "nsre" is at the moment at regionserver:1 256 c.metaRegionInfo.SetClient(rc1) 257 origlReg.SetClient(rc1) 258 // marking unavailable to simulate error 259 origlReg.MarkUnavailable() 260 c.regions.put(origlReg) 261 262 c.reestablishRegion(origlReg) 263 264 if len(c.clients.regions) != 1 { 265 t.Errorf("Expected 1 client in cache, got %d", len(c.clients.regions)) 266 } 267 268 if c.regions.regions.Len() != 1 { 269 // expecting only one region because meta isn't in cache, it's hard-coded 270 t.Errorf("Expected 1 regions in cache, got %d", c.regions.regions.Len()) 271 } 272 273 // check the we have the region in regions cache 274 _, ok := c.regions.regions.Get( 275 []byte("nsre,,1434573235908.56f833d5569a27c7a43fbf547b4924a4.")) 276 if !ok { 277 t.Error("Expected region is not in the cache") 278 } 279 280 // check region is available and it's client is empty since we 281 // need to release all the waiting callers 282 if origlReg.IsUnavailable() { 283 t.Error("Expected original region to be available") 284 } 285 286 if origlReg.Client() != rc1 { 287 t.Error("Expected original region the same client") 288 } 289 } 290 291 func TestEstablishRegionDialFail(t *testing.T) { 292 ctrl := test.NewController(t) 293 defer ctrl.Finish() 294 295 c := newMockClient(nil) 296 297 rcFailDial := mockRegion.NewMockRegionClient(ctrl) 298 // return an error to make sure we lookup an new client 299 rcFailDial.EXPECT().Dial(gomock.Any()).Return(errors.New("ooops")).AnyTimes() 300 rcFailDial.EXPECT().Addr().Return("regionserver:1").AnyTimes() 301 rcFailDial.EXPECT().String().Return("regionserver:1").AnyTimes() 302 303 // second client returns that region has been dead 304 // this is a success case to make sure we ever obtain a new client and not 305 // just stuck looking up in cache 306 rcDialCancel := mockRegion.NewMockRegionClient(ctrl) 307 rcDialCancel.EXPECT().Dial(gomock.Any()).Return(context.Canceled) 308 rcDialCancel.EXPECT().Addr().Return("regionserver:1").AnyTimes() 309 rcDialCancel.EXPECT().String().Return("reginserver:1").AnyTimes() 310 311 newRegionClientFnCallCount := 0 312 c.newRegionClientFn = func(_ string, _ region.ClientType, _ int, _ time.Duration, 313 _ string, _ time.Duration, _ compression.Codec, 314 _ func(ctx context.Context, network, addr string) (net.Conn, error), 315 _ *slog.Logger) hrpc.RegionClient { 316 var rc hrpc.RegionClient 317 if newRegionClientFnCallCount == 0 { 318 rc = rcFailDial 319 } else { 320 // if there was a bug with cache updates, we would never get into this case 321 rc = rcDialCancel 322 } 323 newRegionClientFnCallCount++ 324 return rc 325 } 326 327 reg := region.NewInfo( 328 0, nil, []byte("test1"), []byte("test1,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."), 329 nil, nil) 330 reg.MarkUnavailable() 331 332 // inject a fake regionserver client and fake region into cache 333 // pretend regionserver:0 has meta table 334 rc1 := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 335 c.metaRegionInfo.SetClient(rc1) 336 337 // should get stuck if the region is never established 338 c.establishRegion(reg, "regionserver:1") 339 340 if len(c.clients.regions) != 2 { 341 t.Errorf("Expected 2 clients in cache, got %d", len(c.clients.regions)) 342 } 343 344 if reg.IsUnavailable() { 345 t.Error("Expected region to be available") 346 } 347 } 348 349 func TestEstablishClientConcurrent(t *testing.T) { 350 // test that the same client isn't added when establishing it concurrently 351 // if there's a race, this test will only fail sometimes 352 ctrl := test.NewController(t) 353 defer ctrl.Finish() 354 // we expect to ask zookeeper for where metaregion is 355 c := newMockClient(mockZk.NewMockClient(ctrl)) 356 // pre-create fake regions to establish 357 numRegions := 1000 358 regions := make([]hrpc.RegionInfo, numRegions) 359 for i := range regions { 360 r := region.NewInfo( 361 0, 362 nil, 363 []byte("test"), 364 []byte(fmt.Sprintf("test,%d,1434573235908.yoloyoloyoloyoloyoloyoloyoloyolo.", i)), 365 nil, nil) 366 r.MarkUnavailable() 367 regions[i] = r 368 } 369 370 // all of the regions have the same region client 371 var wg sync.WaitGroup 372 for _, r := range regions { 373 r := r 374 wg.Add(1) 375 go func() { 376 c.establishRegion(r, "regionserver:1") 377 wg.Done() 378 }() 379 } 380 wg.Wait() 381 382 if len(c.clients.regions) != 1 { 383 t.Fatalf("Expected to have only 1 client in cache, got %d", len(c.clients.regions)) 384 } 385 386 for rc, rs := range c.clients.regions { 387 if len(rs) != numRegions { 388 t.Errorf("Expected to have %d regions, got %d", numRegions, len(rs)) 389 } 390 // check that all regions have the same client set and are available 391 for _, r := range regions { 392 if r.Client() != rc { 393 t.Errorf("Region %q has unexpected client %s", r.Name(), rc.Addr()) 394 } 395 if r.IsUnavailable() { 396 t.Errorf("Expected region %s to be available", r.Name()) 397 } 398 } 399 } 400 } 401 402 func TestEstablishServerErrorDuringProbe(t *testing.T) { 403 ctrl := test.NewController(t) 404 defer ctrl.Finish() 405 c := newMockClient(nil) 406 407 // pretend regionserver:0 has meta table 408 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 409 c.metaRegionInfo.SetClient(rc) 410 411 mockCall := mock.NewMockCall(ctrl) 412 mockCall.EXPECT().Context().Return(context.Background()).AnyTimes() 413 mockCall.EXPECT().Description().AnyTimes() 414 mockCall.EXPECT().Table().Return([]byte("down")).AnyTimes() 415 mockCall.EXPECT().Key().Return([]byte("yolo")).AnyTimes() 416 mockCall.EXPECT().SetRegion(gomock.Any()).AnyTimes() 417 result := make(chan hrpc.RPCResult, 1) 418 // pretend that response is successful 419 expMsg := &pb.GetResponse{} 420 result <- hrpc.RPCResult{Msg: expMsg} 421 mockCall.EXPECT().ResultChan().Return(result).Times(1) 422 msg, err := c.SendRPC(mockCall) 423 if err != nil { 424 t.Errorf("Unexpected error: %s", err) 425 } 426 427 if !proto.Equal(expMsg, msg) { 428 t.Errorf("expected %v, got %v", expMsg, msg) 429 } 430 431 if len(c.clients.regions) != 2 { 432 t.Errorf("expected to have 2 clients in cache, got %d", len(c.clients.regions)) 433 } 434 } 435 436 // TestSendRPCToRegionClientDownDelayed check that the we don't replace a newly 437 // looked up client (by other goroutine) in clients cache if we are too slow 438 // at finding out that our old client died. 439 func TestSendRPCToRegionClientDownDelayed(t *testing.T) { 440 ctrl := test.NewController(t) 441 defer ctrl.Finish() 442 443 zkClient := mockZk.NewMockClient(ctrl) 444 zkClient.EXPECT().LocateResource(zk.Meta).Return("regionserver:1", nil).AnyTimes() 445 c := newMockClient(zkClient) 446 447 // create region with mock client 448 origlReg := region.NewInfo( 449 0, nil, []byte("test1"), 450 []byte("test1,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 451 nil, nil) 452 c.regions.put(origlReg) 453 rc := mockRegion.NewMockRegionClient(ctrl) 454 rc.EXPECT().String().Return("mock region client").AnyTimes() 455 c.clients.put("regionserver:1", origlReg, func() hrpc.RegionClient { 456 return rc 457 }) 458 origlReg.SetClient(rc) 459 460 mockCall := mock.NewMockCall(ctrl) 461 mockCall.EXPECT().Region().Return(origlReg).Times(1) 462 result := make(chan hrpc.RPCResult, 1) 463 mockCall.EXPECT().ResultChan().Return(result).Times(1) 464 465 rc2 := newRegionClientFn("regionserver:1")() 466 rc.EXPECT().QueueRPC(mockCall).Times(1).Do(func(rpc hrpc.Call) { 467 // remove old client from clients cache 468 c.clients.clientDown(rc) 469 // replace client in region with new client 470 // this simulate other rpc toggling client reestablishment 471 c.regions.put(origlReg) 472 c.clients.put("regionserver:1", origlReg, func() hrpc.RegionClient { return rc2 }) 473 474 origlReg.SetClient(rc2) 475 476 // return ServerError from QueueRPC, to emulate dead client 477 result <- hrpc.RPCResult{Error: region.ServerError{}} 478 }) 479 480 _, err := c.sendRPCToRegionClient(context.Background(), mockCall, rc) 481 switch err.(type) { 482 case region.ServerError, region.NotServingRegionError: 483 default: 484 t.Errorf("Got unexpected error: %v", err) 485 } 486 // Wait for establishRegion to complete 487 ch := origlReg.AvailabilityChan() 488 if ch != nil { 489 <-ch 490 } 491 // check that we did not down new client 492 if len(c.clients.regions) != 1 { 493 t.Errorf("There are %d cached clients:", len(c.clients.regions)) 494 for rc := range c.clients.regions { 495 t.Errorf("%s", rc.String()) 496 } 497 } 498 _, ok := c.clients.regions[rc2] 499 if !ok { 500 t.Error("rc2 is not in clients cache") 501 } 502 } 503 504 func TestReestablishDeadRegion(t *testing.T) { 505 ctrl := test.NewController(t) 506 defer ctrl.Finish() 507 // setting zookeeper client to nil because we don't 508 // expect for it to be called 509 c := newMockClient(nil) 510 // here we assume that this region was removed from 511 // regions cache and thereby is considered dead 512 reg := region.NewInfo( 513 0, 514 nil, 515 []byte("test"), 516 []byte("test,,1434573235908.yoloyoloyoloyoloyoloyoloyoloyolo."), 517 nil, nil) 518 reg.MarkDead() 519 520 rc1 := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 521 c.clients.put("regionserver:0", reg, newRegionClientFn("regionserver:0")) 522 523 // pretend regionserver:0 has meta table 524 c.metaRegionInfo.SetClient(rc1) 525 526 reg.MarkUnavailable() 527 528 // pretend that there's a race condition right after we removed region 529 // from regions cache a client dies that we haven't removed from 530 // clients cache yet 531 c.reestablishRegion(reg) // should exit TODO: check fast 532 533 // should not put anything in cache, we specifically get a region with new name 534 if c.regions.regions.Len() != 0 { 535 t.Errorf("Expected to have no region in cache, got %d", c.regions.regions.Len()) 536 } 537 538 // should not establish any clients 539 if len(c.clients.regions) != 1 { 540 t.Errorf("Expected to have 1 client in cache, got %d", len(c.clients.regions)) 541 } 542 543 if reg.Client() != nil { 544 t.Error("Expected to have no client established") 545 } 546 547 // should have reg as available 548 if reg.IsUnavailable() { 549 t.Error("Expected region to be available") 550 } 551 } 552 553 func TestFindRegion(t *testing.T) { 554 // TODO: check regions are deleted from client's cache 555 // when regions are replaced 556 tcases := []struct { 557 before []hrpc.RegionInfo 558 after []string 559 establish bool 560 regName string 561 err error 562 }{ 563 { // nothing in cache 564 before: nil, 565 after: []string{"test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."}, 566 establish: true, 567 regName: "test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4.", 568 }, 569 { // older region in cache 570 before: []hrpc.RegionInfo{ 571 region.NewInfo(1, nil, []byte("test"), 572 []byte("test,,1.yoloyoloyoloyoloyoloyoloyoloyolo."), nil, nil), 573 }, 574 after: []string{"test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."}, 575 establish: true, 576 regName: "test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4.", 577 }, 578 { // younger region in cache 579 before: []hrpc.RegionInfo{ 580 region.NewInfo(9999999999999, nil, []byte("test"), 581 []byte("test,,9999999999999.yoloyoloyoloyoloyoloyoloyoloyolo."), nil, nil), 582 }, 583 after: []string{"test,,9999999999999.yoloyoloyoloyoloyoloyoloyoloyolo."}, 584 establish: false, 585 }, 586 { // overlapping younger region in cache, passed key is not in that region 587 before: []hrpc.RegionInfo{ 588 region.NewInfo(9999999999999, nil, []byte("test"), 589 []byte("test,,9999999999999.yoloyoloyoloyoloyoloyoloyoloyolo."), 590 nil, []byte("foo")), 591 }, 592 after: []string{"test,,9999999999999.yoloyoloyoloyoloyoloyoloyoloyolo."}, 593 establish: false, 594 }, 595 } 596 597 ctrl := test.NewController(t) 598 defer ctrl.Finish() 599 // setting zookeeper client to nil because we don't 600 // expect for it to be called 601 c := newMockClient(nil) 602 // pretend regionserver:0 has meta table 603 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 604 c.metaRegionInfo.SetClient(rc) 605 606 ctx := context.Background() 607 testTable := []byte("test") 608 testKey := []byte("yolo") 609 for i, tcase := range tcases { 610 t.Run(strconv.Itoa(i), func(t *testing.T) { 611 c.regions.regions.Clear() 612 // set up initial cache 613 for _, region := range tcase.before { 614 c.regions.put(region) 615 } 616 617 reg, err := c.findRegion(ctx, testTable, testKey) 618 if err != tcase.err { 619 t.Fatalf("Expected error %v, got %v", tcase.err, err) 620 } 621 if len(tcase.regName) == 0 { 622 if reg != nil { 623 t.Errorf("Expected nil region, got %v", reg) 624 } 625 } else if string(reg.Name()) != tcase.regName { 626 t.Errorf("Expected region with name %q, got region %v", 627 tcase.regName, reg) 628 } 629 630 // check cache 631 if len(tcase.after) != c.regions.regions.Len() { 632 t.Errorf("Expected to have %d regions in cache, got %d", 633 len(tcase.after), c.regions.regions.Len()) 634 } 635 for _, rn := range tcase.after { 636 if _, ok := c.regions.regions.Get([]byte(rn)); !ok { 637 t.Errorf("Expected region %q to be in regions cache", rn) 638 } 639 } 640 // check client was looked up 641 if tcase.establish { 642 if ch := reg.AvailabilityChan(); ch != nil { 643 // if still establishing, wait 644 <-ch 645 } 646 rc2 := reg.Client() 647 if rc2 == nil { 648 t.Errorf("Expected region %q to establish a client", reg.Name()) 649 return 650 } 651 if rc2.Addr() != "regionserver:2" { 652 t.Errorf("Expected regionserver:2, got %q", rc2.Addr()) 653 } 654 } 655 }) 656 } 657 658 } 659 660 func TestErrCannotFindRegion(t *testing.T) { 661 c := newMockClient(nil) 662 663 // pretend regionserver:0 has meta table 664 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 665 c.metaRegionInfo.SetClient(rc) 666 667 // add young and small region to cache 668 origlReg := region.NewInfo(1434573235910, nil, []byte("test"), 669 []byte("test,yolo,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("yolo"), nil) 670 c.regions.put(origlReg) 671 rc = c.clients.put("regionserver:0", origlReg, newRegionClientFn("regionserver:0")) 672 origlReg.SetClient(rc) 673 674 // request a key not in the "yolo" region. 675 get, err := hrpc.NewGetStr(context.Background(), "test", "meow") 676 if err != nil { 677 t.Fatal(err) 678 } 679 // it should lookup a new older region (1434573235908) that overlaps with the one in cache. 680 // However, it shouldn't be put into cache, as it's older, resulting in a new lookup, 681 // evetually leading to ErrCannotFindRegion. 682 _, err = c.Get(get) 683 if err != ErrCannotFindRegion { 684 t.Errorf("Expected error %v, got error %v", ErrCannotFindRegion, err) 685 } 686 } 687 688 func TestMetaLookupTableNotFound(t *testing.T) { 689 c := newMockClient(nil) 690 // pretend regionserver:0 has meta table 691 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 692 c.metaRegionInfo.SetClient(rc) 693 694 _, _, err := c.metaLookup(context.Background(), []byte("tablenotfound"), []byte(t.Name())) 695 if err != TableNotFound { 696 t.Errorf("Expected error %v, got error %v", TableNotFound, err) 697 } 698 } 699 700 func TestMetaLookupCanceledContext(t *testing.T) { 701 c := newMockClient(nil) 702 // pretend regionserver:0 has meta table 703 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 704 c.metaRegionInfo.SetClient(rc) 705 706 ctx, cancel := context.WithCancel(context.Background()) 707 cancel() 708 _, _, err := c.metaLookup(ctx, []byte("tablenotfound"), []byte(t.Name())) 709 if err != context.Canceled { 710 t.Errorf("Expected error %v, got error %v", context.Canceled, err) 711 } 712 } 713 714 func TestMetaLookupAllRegionsCanceledContext(t *testing.T) { 715 c := newMockClient(nil) 716 // pretend regionserver:0 has meta table 717 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 718 c.metaRegionInfo.SetClient(rc) 719 720 ctx, cancel := context.WithCancel(context.Background()) 721 cancel() 722 _, err := c.metaLookupForTable(ctx, []byte("tablenotfound")) 723 if err != context.Canceled { 724 t.Errorf("Expected error %v, got error %v", context.Canceled, err) 725 } 726 } 727 728 func TestConcurrentRetryableError(t *testing.T) { 729 ctrl := test.NewController(t) 730 defer ctrl.Finish() 731 732 zkc := mockZk.NewMockClient(ctrl) 733 // keep failing on zookeeper lookup 734 zkc.EXPECT().LocateResource(gomock.Any()).Return("", errors.New("ooops")).AnyTimes() 735 c := newMockClient(zkc) 736 // create region with mock clien 737 origlReg := region.NewInfo( 738 0, 739 nil, 740 []byte("test1"), 741 []byte("test1,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), 742 nil, 743 nil, 744 ) 745 746 rc := mockRegion.NewMockRegionClient(ctrl) 747 rc.EXPECT().String().Return("mock region client").AnyTimes() 748 rc.EXPECT().Addr().Return("host:1234").AnyTimes() 749 newRC := func() hrpc.RegionClient { 750 return rc 751 } 752 c.clients.put("host:1234", c.metaRegionInfo, newRC) 753 c.metaRegionInfo.SetClient(rc) 754 c.regions.put(origlReg) 755 c.clients.put("host:1234", origlReg, newRC) 756 origlReg.SetClient(rc) 757 758 numCalls := 100 759 rc.EXPECT().QueueRPC(gomock.Any()).MinTimes(1) 760 761 calls := make([]hrpc.Call, numCalls) 762 for i := range calls { 763 mockCall := mock.NewMockCall(ctrl) 764 mockCall.EXPECT().SetRegion(origlReg).AnyTimes() 765 mockCall.EXPECT().Region().Return(origlReg).AnyTimes() 766 result := make(chan hrpc.RPCResult, 1) 767 result <- hrpc.RPCResult{Error: region.NotServingRegionError{}} 768 mockCall.EXPECT().ResultChan().Return(result).AnyTimes() 769 calls[i] = mockCall 770 } 771 772 var wg sync.WaitGroup 773 for _, mockCall := range calls { 774 wg.Add(1) 775 go func(mockCall hrpc.Call) { 776 _, err := c.sendRPCToRegionClient(context.Background(), mockCall, rc) 777 if _, ok := err.(region.NotServingRegionError); !ok { 778 t.Errorf("Got unexpected error: %v", err) 779 } 780 wg.Done() 781 }(mockCall) 782 } 783 wg.Wait() 784 origlReg.MarkDead() 785 if ch := origlReg.AvailabilityChan(); ch != nil { 786 <-ch 787 } 788 } 789 790 func TestProbeKey(t *testing.T) { 791 regions := []hrpc.RegionInfo{ 792 region.NewInfo(0, nil, nil, nil, nil, nil), 793 region.NewInfo(0, nil, nil, nil, nil, []byte("yolo")), 794 region.NewInfo(0, nil, nil, nil, []byte("swag"), nil), 795 region.NewInfo(0, nil, nil, nil, []byte("swag"), []byte("yolo")), 796 region.NewInfo(0, nil, nil, nil, []byte("aaaaaaaaaaaaaa"), []byte("bbbb")), 797 region.NewInfo(0, nil, nil, nil, []byte("aaaa"), []byte("bbbbbbb")), 798 region.NewInfo(0, nil, nil, nil, []byte("aaaa"), []byte("aab")), 799 region.NewInfo(0, nil, nil, nil, []byte("aaa"), []byte("aaaaaaaaaa")), 800 region.NewInfo(0, nil, nil, nil, []byte{255}, nil), 801 region.NewInfo(0, nil, nil, nil, []byte{255, 255}, nil), 802 } 803 804 for _, reg := range regions { 805 key := probeKey(reg) 806 isGreaterThanStartOfTable := len(reg.StartKey()) == 0 && len(key) > 0 807 isGreaterThanStartKey := bytes.Compare(reg.StartKey(), key) < 0 808 isLessThanEndOfTable := len(reg.StopKey()) == 0 && 809 bytes.Compare(key, bytes.Repeat([]byte{255}, len(key))) < 0 810 isLessThanStopKey := bytes.Compare(key, reg.StopKey()) < 0 811 if (isGreaterThanStartOfTable || isGreaterThanStartKey) && 812 (isLessThanEndOfTable || isLessThanStopKey) { 813 continue 814 } 815 t.Errorf("key %q is not within bounds of region %s: %v %v %v %v\n", key, reg, 816 isGreaterThanStartOfTable, isGreaterThanStartKey, 817 isLessThanEndOfTable, isLessThanStopKey) 818 } 819 } 820 821 var ( 822 description = "GET" 823 result = "SUCCESS" 824 o prometheus.Observer 825 ) 826 827 func BenchmarkPrometheusWithLabelValues(b *testing.B) { 828 b.ReportAllocs() 829 for i := 0; i < b.N; i++ { 830 o = operationDurationSeconds.WithLabelValues( 831 description, 832 result, 833 ) 834 } 835 } 836 837 func BenchmarkPrometheusWith(b *testing.B) { 838 b.ReportAllocs() 839 for i := 0; i < b.N; i++ { 840 o = operationDurationSeconds.With(prometheus.Labels{ 841 "operation": description, 842 "result": result, 843 }) 844 } 845 } 846 847 func TestSendBatchBasic(t *testing.T) { 848 ctrl := test.NewController(t) 849 defer ctrl.Finish() 850 // we expect to ask zookeeper for where metaregion is 851 zkClient := mockZk.NewMockClient(ctrl) 852 zkClient.EXPECT().LocateResource(zk.Meta).Return("regionserver:1", nil).MinTimes(1) 853 c := newMockClient(zkClient) 854 855 call, err := hrpc.NewPutStr(context.Background(), "test", "theKey", nil) 856 if err != nil { 857 t.Fatal(err) 858 } 859 // pretend that response is successful 860 expMsg := &pb.MutateResponse{} 861 call.ResultChan() <- hrpc.RPCResult{Msg: expMsg} 862 msg, ok := c.SendBatch(context.Background(), []hrpc.Call{call}) 863 if !ok { 864 t.Fatal(msg[0].Error) 865 } 866 if !proto.Equal(expMsg, msg[0].Msg) { 867 t.Errorf("expected %v, got %v", expMsg, msg) 868 } 869 870 if len(c.clients.regions) != 2 { 871 t.Errorf("Expected 2 clients in cache, got %d", len(c.clients.regions)) 872 } 873 874 // addr -> region name 875 expClients := map[string]string{ 876 "regionserver:1": "hbase:meta,,1", 877 "regionserver:2": "test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4.", 878 } 879 880 // make sure those are the right clients 881 for c, rs := range c.clients.regions { 882 name, ok := expClients[c.Addr()] 883 if !ok { 884 t.Errorf("Got unexpected client %s in cache", c.Addr()) 885 continue 886 } 887 if len(rs) != 1 { 888 t.Errorf("Expected to have only 1 region in cache for client %s", c.Addr()) 889 continue 890 } 891 for r := range rs { 892 if string(r.Name()) != name { 893 t.Errorf("Unexpected name of region %q for client %s, expected %q", 894 r.Name(), c.Addr(), name) 895 } 896 // check bidirectional mapping, they have to be the same objects 897 rc := r.Client() 898 if c != rc { 899 t.Errorf("Invalid bidirectional mapping: forward=%s, backward=%s", 900 c.Addr(), rc.Addr()) 901 } 902 } 903 } 904 905 if c.regions.regions.Len() != 1 { 906 // expecting only one region because meta isn't in cache, it's hard-coded 907 t.Errorf("Expected 1 regions in cache, got %d", c.regions.regions.Len()) 908 } 909 910 } 911 912 func TestSendBatchBadInput(t *testing.T) { 913 ctrl := test.NewController(t) 914 defer ctrl.Finish() 915 916 zkc := mockZk.NewMockClient(ctrl) 917 zkc.EXPECT().LocateResource(zk.Meta).Return("regionserver:1", nil).AnyTimes() 918 c := newMockClient(zkc) 919 920 newRPC := func(table string, batchable bool) hrpc.Call { 921 if batchable { 922 rpc, err := hrpc.NewPutStr(context.Background(), table, "key", 923 map[string]map[string][]byte{"cf": {"foo": []byte("bar")}}) 924 if err != nil { 925 t.Fatal(err) 926 } 927 return rpc 928 } 929 rpc, err := hrpc.NewScanStr(context.Background(), table) 930 if err != nil { 931 t.Fatal(err) 932 } 933 return rpc 934 } 935 936 rpc1 := newRPC("table", true) 937 rpc2 := newRPC("table", true) 938 939 for _, tc := range []struct { 940 name string 941 batch []hrpc.Call 942 expErr []string 943 }{{ 944 name: "duplicate", 945 batch: []hrpc.Call{rpc1, rpc1}, 946 expErr: []string{NotExecutedError.Error(), "duplicate call"}, 947 }, { 948 name: "duplicate2", 949 batch: []hrpc.Call{rpc1, rpc2, rpc2, newRPC("table", true), rpc1}, 950 expErr: []string{NotExecutedError.Error(), NotExecutedError.Error(), 951 "duplicate call", NotExecutedError.Error(), "duplicate call"}, 952 }, { 953 name: "tables", 954 batch: []hrpc.Call{newRPC("table", true), newRPC("different_table", true)}, 955 expErr: []string{NotExecutedError.Error(), "multiple tables"}, 956 }, { 957 name: "batchable", 958 batch: []hrpc.Call{newRPC("table", false)}, 959 expErr: []string{"non-batchable"}, 960 }, { 961 name: "various errors", 962 batch: []hrpc.Call{rpc1, 963 newRPC("table", false), 964 rpc1, 965 newRPC("table2", true), 966 newRPC("table", true)}, 967 expErr: []string{NotExecutedError.Error(), 968 "non-batchable", 969 "duplicate call", 970 "multiple tables", 971 NotExecutedError.Error()}, 972 }} { 973 t.Run(tc.name, func(t *testing.T) { 974 if len(tc.batch) != len(tc.expErr) { 975 t.Fatalf("test case provides mismatched batch (%d) and expErr (%d) sizes", 976 len(tc.batch), len(tc.expErr)) 977 } 978 979 results, ok := c.SendBatch(context.Background(), tc.batch) 980 if ok { 981 t.Errorf("expected !ok from SendBatch, got %t", ok) 982 } 983 if len(results) != len(tc.batch) { 984 t.Fatalf("result size (%d) does not match batch size (%d)", 985 len(results), len(tc.batch)) 986 } 987 for i, res := range results { 988 if res.Error == nil { 989 t.Errorf("expected error in res[%d], but got nil for request %v", 990 i, tc.batch[i]) 991 continue 992 } 993 if !strings.Contains(res.Error.Error(), tc.expErr[i]) { 994 t.Errorf("expected error to contain %q, but got %q", tc.expErr[i], res.Error) 995 } 996 } 997 }) 998 } 999 } 1000 1001 // TestFindClient ensures findClients groups RPCs in a batch by region 1002 // server and preserves the ordering of requests. And that each RPC 1003 // has its region assigned. 1004 func TestFindClients(t *testing.T) { 1005 c := newMockClient(nil) 1006 // pretend regionserver:0 has meta table 1007 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 1008 c.metaRegionInfo.SetClient(rc) 1009 1010 registerRegion := func(reg hrpc.RegionInfo, addr string) { 1011 rc := c.clients.put(addr, reg, newRegionClientFn(addr)) 1012 reg.SetClient(rc) 1013 overlaps, replaced := c.regions.put(reg) 1014 if len(overlaps) > 0 { 1015 t.Fatalf("overlaps: %v replaced: %t", overlaps, replaced) 1016 } 1017 } 1018 // Create three region servers, each with three regions 1019 regA := region.NewInfo(1434573235910, nil, []byte("test"), 1020 []byte("test,a,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("a"), []byte("az")) 1021 regB := region.NewInfo(1434573235910, nil, []byte("test"), 1022 []byte("test,b,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("b"), []byte("bz")) 1023 regC := region.NewInfo(1434573235910, nil, []byte("test"), 1024 []byte("test,c,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("c"), []byte("cz")) 1025 for _, reg := range []hrpc.RegionInfo{regA, regB, regC} { 1026 registerRegion(reg, "regionserver:0") 1027 } 1028 regD := region.NewInfo(1434573235910, nil, []byte("test"), 1029 []byte("test,d,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("d"), []byte("dz")) 1030 regE := region.NewInfo(1434573235910, nil, []byte("test"), 1031 []byte("test,e,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("e"), []byte("ez")) 1032 regF := region.NewInfo(1434573235910, nil, []byte("test"), 1033 []byte("test,f,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("f"), []byte("fz")) 1034 for _, reg := range []hrpc.RegionInfo{regD, regE, regF} { 1035 registerRegion(reg, "regionserver:1") 1036 } 1037 regG := region.NewInfo(1434573235910, nil, []byte("test"), 1038 []byte("test,g,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("g"), []byte("gz")) 1039 regH := region.NewInfo(1434573235910, nil, []byte("test"), 1040 []byte("test,h,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("h"), []byte("hz")) 1041 regI := region.NewInfo(1434573235910, nil, []byte("test"), 1042 []byte("test,i,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("i"), []byte("iz")) 1043 for _, reg := range []hrpc.RegionInfo{regG, regH, regI} { 1044 registerRegion(reg, "regionserver:2") 1045 } 1046 1047 if l := len(c.clients.regions); l != 3 { 1048 t.Errorf("expected 3 region clients, got %d", l) 1049 t.Logf("%v", c.clients.regions) 1050 } 1051 1052 if l := c.regions.regions.Len(); l != 9 { 1053 t.Errorf("expected 9 regions, got %d", l) 1054 } 1055 1056 newRPC := func(key string) hrpc.Call { 1057 rpc, err := hrpc.NewPutStr(context.Background(), "test", key, 1058 map[string]map[string][]byte{"cf": {"foo": []byte("bar")}}) 1059 if err != nil { 1060 t.Fatal(err) 1061 } 1062 return rpc 1063 } 1064 1065 type kr struct { 1066 key string 1067 reg hrpc.RegionInfo 1068 } 1069 1070 type testCase struct { 1071 name string 1072 batch []hrpc.Call 1073 exp map[string][]kr 1074 expErrs []string 1075 } 1076 1077 for _, tc := range []testCase{{ 1078 name: "one_rc", 1079 batch: []hrpc.Call{ 1080 newRPC("aa"), 1081 newRPC("ba"), 1082 newRPC("ca"), 1083 }, 1084 exp: map[string][]kr{ 1085 "regionserver:0": { 1086 {"aa", regA}, 1087 {"ba", regB}, 1088 {"ca", regC}, 1089 }, 1090 }, 1091 }, { 1092 name: "three_rc", 1093 batch: []hrpc.Call{ 1094 newRPC("aa"), 1095 newRPC("da"), 1096 newRPC("ga"), 1097 newRPC("ab"), 1098 }, 1099 exp: map[string][]kr{ 1100 "regionserver:0": { 1101 {"aa", regA}, 1102 {"ab", regA}, 1103 }, 1104 "regionserver:1": { 1105 {"da", regD}, 1106 }, 1107 "regionserver:2": { 1108 {"ga", regG}, 1109 }, 1110 }, 1111 }, { 1112 name: "error end", 1113 batch: []hrpc.Call{ 1114 newRPC("aa"), 1115 newRPC("da"), 1116 newRPC("ga"), 1117 newRPC("zz"), // missing region 1118 }, 1119 expErrs: []string{"", "", "", "cannot find region"}, 1120 }, { 1121 name: "error middle", 1122 batch: []hrpc.Call{ 1123 newRPC("aa"), 1124 newRPC("da"), 1125 newRPC("zz"), // missing region 1126 newRPC("ga"), 1127 }, 1128 expErrs: []string{"", "", "cannot find region", ""}, 1129 }, { 1130 name: "error beginning", 1131 batch: []hrpc.Call{ 1132 newRPC("zz"), // missing region 1133 newRPC("aa"), 1134 newRPC("da"), 1135 newRPC("ga"), 1136 }, 1137 expErrs: []string{"cannot find region", "", "", ""}, 1138 }} { 1139 t.Run(tc.name, func(t *testing.T) { 1140 results := make([]hrpc.RPCResult, len(tc.batch)) 1141 got, ok := c.findClients(context.Background(), tc.batch, results) 1142 if ok && len(tc.expErrs) > 0 { 1143 t.Fatalf("expected error, %v", results[3]) 1144 1145 } else if !ok && len(tc.expErrs) == 0 { 1146 t.Fatalf("unexpected !ok: %v", results) 1147 } else if !ok { 1148 for i, res := range results { 1149 expErr := tc.expErrs[i] 1150 if expErr == "" && res.Error != nil { 1151 t.Errorf("unexpected error: %v", res.Error) 1152 } 1153 if expErr == "" { 1154 continue 1155 } 1156 if !strings.Contains(res.Error.Error(), expErr) { 1157 t.Errorf("expected error to contain %q, got: %v", expErr, res.Error) 1158 } 1159 } 1160 return 1161 } 1162 1163 for rc, rpcs := range got { 1164 expRPCs, ok := tc.exp[rc.Addr()] 1165 if !ok { 1166 t.Errorf("unexpected region client: %q", rc.Addr()) 1167 } 1168 delete(tc.exp, rc.Addr()) 1169 for i, rpc := range rpcs { 1170 if i >= len(expRPCs) { 1171 t.Errorf("unexpected RPC for region client %q: %s", 1172 rc.Addr(), rpc.Key()) 1173 continue 1174 } 1175 expRPC := expRPCs[i] 1176 if string(rpc.Key()) != expRPC.key { 1177 t.Errorf("for region client %q expected rpc %s, but got %s", 1178 rc.Addr(), expRPC.key, rpc.Key()) 1179 } 1180 if rpc.Region() != expRPC.reg { 1181 t.Errorf("for region client %q rpc %s, expected reg %v but got %v", 1182 rc.Addr(), expRPC.key, rpc.Region(), expRPC.reg) 1183 } 1184 } 1185 if len(expRPCs) > len(rpcs) { 1186 for _, rpc := range expRPCs[len(rpcs):] { 1187 t.Errorf("expected RPC for region client %q: %s", 1188 rc.Addr(), rpc.key) 1189 } 1190 } 1191 } 1192 for rc, rpcs := range tc.exp { 1193 t.Errorf("expected rpcs for region client %q: %v", rc, rpcs) 1194 } 1195 }) 1196 } 1197 } 1198 1199 func TestSendBatchWaitForCompletion(t *testing.T) { 1200 sleepCh := make(chan struct{}) 1201 sleepAndIncreaseBackoffOverride = func(ctx context.Context, backoff time.Duration) ( 1202 time.Duration, error) { 1203 sleepCh <- struct{}{} 1204 return backoff, nil 1205 } 1206 estRegCh := make(chan hrpc.RegionInfo) 1207 establishRegionOverride = func(reg hrpc.RegionInfo, addr string) { 1208 estRegCh <- reg 1209 } 1210 defer func() { 1211 close(sleepCh) // panic any unexpected calls to sleepAndIncreaseBackoff 1212 sleepAndIncreaseBackoffOverride = nil 1213 close(estRegCh) // panic any unexpected calls to establishRegion 1214 establishRegionOverride = nil 1215 }() 1216 1217 c := newMockClient(nil) 1218 // pretend regionserver:0 has meta table 1219 rc := c.clients.put("regionserver:0", c.metaRegionInfo, newRegionClientFn("regionserver:0")) 1220 c.metaRegionInfo.SetClient(rc) 1221 1222 registerRegion := func(reg hrpc.RegionInfo, addr string) { 1223 rc := c.clients.put(addr, reg, newRegionClientFn(addr)) 1224 reg.SetClient(rc) 1225 overlaps, replaced := c.regions.put(reg) 1226 if len(overlaps) > 0 { 1227 t.Fatalf("overlaps: %v replaced: %t", overlaps, replaced) 1228 } 1229 } 1230 // Create three region servers, each with three regions 1231 regA := region.NewInfo(1434573235910, nil, []byte("test"), 1232 []byte("test,a,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("a"), []byte("az")) 1233 regB := region.NewInfo(1434573235910, nil, []byte("test"), 1234 []byte("test,b,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("b"), []byte("bz")) 1235 regC := region.NewInfo(1434573235910, nil, []byte("test"), 1236 []byte("test,c,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("c"), []byte("cz")) 1237 for _, reg := range []hrpc.RegionInfo{regA, regB, regC} { 1238 registerRegion(reg, "regionserver:0") 1239 } 1240 regD := region.NewInfo(1434573235910, nil, []byte("test"), 1241 []byte("test,d,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("d"), []byte("dz")) 1242 regE := region.NewInfo(1434573235910, nil, []byte("test"), 1243 []byte("test,e,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("e"), []byte("ez")) 1244 regF := region.NewInfo(1434573235910, nil, []byte("test"), 1245 []byte("test,f,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("f"), []byte("fz")) 1246 for _, reg := range []hrpc.RegionInfo{regD, regE, regF} { 1247 registerRegion(reg, "regionserver:1") 1248 } 1249 regG := region.NewInfo(1434573235910, nil, []byte("test"), 1250 []byte("test,g,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("g"), []byte("gz")) 1251 regH := region.NewInfo(1434573235910, nil, []byte("test"), 1252 []byte("test,h,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("h"), []byte("hz")) 1253 regI := region.NewInfo(1434573235910, nil, []byte("test"), 1254 []byte("test,i,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("i"), []byte("iz")) 1255 for _, reg := range []hrpc.RegionInfo{regG, regH, regI} { 1256 registerRegion(reg, "regionserver:2") 1257 } 1258 1259 if l := len(c.clients.regions); l != 3 { 1260 t.Errorf("expected 3 region clients, got %d", l) 1261 t.Logf("%v", c.clients.regions) 1262 } 1263 1264 if l := c.regions.regions.Len(); l != 9 { 1265 t.Errorf("expected 9 regions, got %d", l) 1266 } 1267 1268 newRPC := func(key string) hrpc.Call { 1269 rpc, err := hrpc.NewPutStr(context.Background(), "test", key, 1270 map[string]map[string][]byte{"cf": {"foo": []byte("bar")}}) 1271 if err != nil { 1272 t.Fatal(err) 1273 } 1274 return rpc 1275 } 1276 1277 t.Run("success", func(t *testing.T) { 1278 batch := make([]hrpc.Call, 9) 1279 for i := 0; i < 9; i++ { 1280 // Create an RPC for each region, "a"-"i" 1281 batch[i] = newRPC(string(rune('a' + i))) 1282 } 1283 var ( 1284 result []hrpc.RPCResult 1285 ok bool 1286 done = make(chan struct{}) 1287 ) 1288 go func() { 1289 result, ok = c.SendBatch(context.Background(), batch) 1290 close(done) 1291 }() 1292 for i := 0; i < 9; i++ { 1293 // Using an Int32 as a result. A real result would be a 1294 // PutResponse, but any proto.Message works for the test. 1295 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1296 } 1297 <-done 1298 if !ok { 1299 t.Errorf("unexpected !ok") 1300 } 1301 if len(result) != 9 { 1302 t.Fatalf("unexpected result size: %v", result) 1303 } 1304 for i, r := range result { 1305 if r.Error != nil { 1306 t.Errorf("unexpected error: %s", r.Error) 1307 continue 1308 } 1309 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1310 t.Errorf("unexpected result: %v", r.Msg) 1311 } 1312 } 1313 }) 1314 1315 t.Run("error all", func(t *testing.T) { 1316 batch := make([]hrpc.Call, 9) 1317 for i := 0; i < 9; i++ { 1318 // Create an RPC for each region, "a"-"i" 1319 key := string(rune('a' + i)) 1320 batch[i] = newRPC(key) 1321 } 1322 var ( 1323 result []hrpc.RPCResult 1324 ok bool 1325 done = make(chan struct{}) 1326 ) 1327 go func() { 1328 result, ok = c.SendBatch(context.Background(), batch) 1329 close(done) 1330 }() 1331 for i := 0; i < 9; i++ { 1332 batch[i].ResultChan() <- hrpc.RPCResult{Error: errors.New("error")} 1333 } 1334 <-done 1335 if ok { 1336 t.Errorf("expected !ok") 1337 } 1338 1339 if len(result) != 9 { 1340 t.Fatalf("unexpected result size: %v", result) 1341 } 1342 for _, r := range result { 1343 if r.Error == nil || r.Error.Error() != "error" { 1344 t.Errorf("expected error but got: %v", r.Error) 1345 } 1346 if r.Msg != nil { 1347 t.Errorf("unexpected Msg: %v", r.Msg) 1348 } 1349 continue 1350 } 1351 }) 1352 1353 t.Run("error one", func(t *testing.T) { 1354 batch := make([]hrpc.Call, 9) 1355 for i := 0; i < 9; i++ { 1356 // Create an RPC for each region, "a"-"i" 1357 key := string(rune('a' + i)) 1358 batch[i] = newRPC(key) 1359 } 1360 var ( 1361 result []hrpc.RPCResult 1362 ok bool 1363 done = make(chan struct{}) 1364 ) 1365 go func() { 1366 result, ok = c.SendBatch(context.Background(), batch) 1367 close(done) 1368 }() 1369 errIndex := rand.Intn(9) 1370 for i := 0; i < 9; i++ { 1371 if i == errIndex { 1372 batch[i].ResultChan() <- hrpc.RPCResult{Error: errors.New("error")} 1373 continue 1374 } 1375 // Using an Int32 as a result. A real result would be a 1376 // PutResponse, but any proto.Message works for the test. 1377 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1378 } 1379 <-done 1380 if ok { 1381 t.Errorf("expected !ok") 1382 } 1383 if len(result) != 9 { 1384 t.Fatalf("unexpected result size: %v", result) 1385 } 1386 for i, r := range result { 1387 if i == errIndex { 1388 if r.Error == nil || r.Error.Error() != "error" { 1389 t.Errorf("expected error but got: %v", r.Error) 1390 } 1391 if r.Msg != nil { 1392 t.Errorf("unexpected Msg: %v", r.Msg) 1393 } 1394 continue 1395 } 1396 if r.Error != nil { 1397 t.Errorf("unexpected error: %s", r.Error) 1398 continue 1399 } 1400 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1401 t.Errorf("unexpected result: %v", r.Msg) 1402 } 1403 } 1404 }) 1405 1406 t.Run("error some", func(t *testing.T) { 1407 batch := make([]hrpc.Call, 9) 1408 for i := 0; i < 9; i++ { 1409 // Create an RPC for each region, "a"-"i" 1410 key := string(rune('a' + i)) 1411 batch[i] = newRPC(key) 1412 } 1413 var ( 1414 result []hrpc.RPCResult 1415 ok bool 1416 done = make(chan struct{}) 1417 ) 1418 go func() { 1419 result, ok = c.SendBatch(context.Background(), batch) 1420 close(done) 1421 }() 1422 1423 // Error on RPCs 0 (first RPC to a region server), 4 (middle 1424 // RPC to a region server), 8 (last to a region server) 1425 for i := 0; i < 9; i++ { 1426 if i%4 == 0 { 1427 batch[i].ResultChan() <- hrpc.RPCResult{Error: errors.New("error")} 1428 continue 1429 } 1430 // Using an Int32 as a result. A real result would be a 1431 // PutResponse, but any proto.Message works for the test. 1432 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1433 } 1434 <-done 1435 if ok { 1436 t.Errorf("expected !ok") 1437 } 1438 if len(result) != 9 { 1439 t.Fatalf("unexpected result size: %v", result) 1440 } 1441 for i, r := range result { 1442 if i%4 == 0 { 1443 if r.Error == nil || r.Error.Error() != "error" { 1444 t.Errorf("expected error but got: %v", r.Error) 1445 } 1446 if r.Msg != nil { 1447 t.Errorf("unexpected Msg: %v", r.Msg) 1448 } 1449 continue 1450 } 1451 if r.Error != nil { 1452 t.Errorf("unexpected error: %s", r.Error) 1453 continue 1454 } 1455 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1456 t.Errorf("unexpected result: %v", r.Msg) 1457 } 1458 } 1459 }) 1460 1461 t.Run("cancel context all", func(t *testing.T) { 1462 batch := make([]hrpc.Call, 9) 1463 for i := 0; i < 9; i++ { 1464 // Create an RPC for each region, "a"-"i" 1465 key := string(rune('a' + i)) 1466 batch[i] = newRPC(key) 1467 } 1468 ctx, cancel := context.WithCancel(context.Background()) 1469 var ( 1470 result []hrpc.RPCResult 1471 ok bool 1472 done = make(chan struct{}) 1473 ) 1474 go func() { 1475 result, ok = c.SendBatch(ctx, batch) 1476 close(done) 1477 }() 1478 cancel() 1479 <-done 1480 if ok { 1481 t.Errorf("expected !ok") 1482 } 1483 if len(result) != 9 { 1484 t.Fatalf("unexpected result size: %v", result) 1485 } 1486 for _, r := range result { 1487 if r.Error == nil || 1488 !errors.Is(r.Error, context.Canceled) { 1489 t.Errorf("expected canceled error but got: %v", r.Error) 1490 } 1491 if r.Msg != nil { 1492 t.Errorf("unexpected Msg: %v", r.Msg) 1493 } 1494 } 1495 }) 1496 1497 t.Run("cancel context some", func(t *testing.T) { 1498 batch := make([]hrpc.Call, 9) 1499 for i := 0; i < 9; i++ { 1500 // Create an RPC for each region, "a"-"i" 1501 key := string(rune('a' + i)) 1502 batch[i] = newRPC(key) 1503 } 1504 ctx, cancel := context.WithCancel(context.Background()) 1505 var ( 1506 result []hrpc.RPCResult 1507 ok bool 1508 done = make(chan struct{}) 1509 ) 1510 go func() { 1511 result, ok = c.SendBatch(ctx, batch) 1512 close(done) 1513 }() 1514 // Send results on RPCs except for 0 (first RPC to a region 1515 // server), 4 (middle RPC to a region server), 8 (last to a 1516 // region server). 1517 for i := 0; i < 9; i++ { 1518 if i%4 == 0 { 1519 continue 1520 } 1521 // Error some responses 1522 if i%2 == 0 { 1523 batch[i].ResultChan() <- hrpc.RPCResult{Error: errors.New("error")} 1524 continue 1525 } 1526 // Using an Int32 as a result. A real result would be a 1527 // MutateResponse, but any proto.Message works for the test. 1528 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1529 } 1530 // Cancel the context. The completed RPCs, should still get 1531 // their results. 1532 cancel() 1533 <-done 1534 if ok { 1535 t.Errorf("expected !ok") 1536 } 1537 if len(result) != 9 { 1538 t.Fatalf("unexpected result size: %v", result) 1539 } 1540 for i, r := range result { 1541 if i%2 == 0 { 1542 expErrStr := "error" 1543 if i%4 == 0 { 1544 expErrStr = context.Canceled.Error() 1545 } 1546 if r.Error == nil || 1547 r.Error.Error() != expErrStr { 1548 t.Errorf("expected canceled error but got: %v", r.Error) 1549 } 1550 if r.Msg != nil { 1551 t.Errorf("unexpected Msg: %v", r.Msg) 1552 } 1553 continue 1554 } 1555 if r.Error != nil { 1556 t.Errorf("unexpected error: %s", r.Error) 1557 continue 1558 } 1559 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1560 t.Errorf("unexpected result: %v", r.Msg) 1561 } 1562 } 1563 }) 1564 1565 t.Run("retryable error some", func(t *testing.T) { 1566 batch := make([]hrpc.Call, 9) 1567 for i := 0; i < 9; i++ { 1568 // Create an RPC for each region, "a"-"i" 1569 key := string(rune('a' + i)) 1570 batch[i] = newRPC(key) 1571 } 1572 var ( 1573 result []hrpc.RPCResult 1574 ok bool 1575 done = make(chan struct{}) 1576 ) 1577 go func() { 1578 result, ok = c.SendBatch(context.Background(), batch) 1579 close(done) 1580 }() 1581 1582 for i := 0; i < 9; i++ { 1583 // Error some responses 1584 if i%2 == 0 { 1585 batch[i].ResultChan() <- hrpc.RPCResult{Error: region.RetryableError{}} 1586 continue 1587 } 1588 // Using an Int32 as a result. A real result would be a 1589 // MutateResponse, but any proto.Message works for the test. 1590 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1591 } 1592 1593 // We should see retries on retryable errors. So, handle the 1594 // sleep and then send back new results. Half succeed, the 1595 // other half get more retryable errors. 1596 <-sleepCh // Expect one call to sleepAndIncreaseBackoff 1597 for i := 0; i < 9; i++ { 1598 if i%4 == 0 { 1599 batch[i].ResultChan() <- hrpc.RPCResult{Error: region.RetryableError{}} 1600 } else if i%2 == 0 { 1601 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1602 } 1603 } 1604 1605 // Send successes for the remaining requests 1606 <-sleepCh // Expect one call to sleepAndIncreaseBackoff 1607 for i := 0; i < 9; i++ { 1608 if i%4 == 0 { 1609 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1610 } 1611 } 1612 1613 <-done 1614 if !ok { 1615 t.Errorf("unexpected !ok") 1616 } 1617 if len(result) != 9 { 1618 t.Fatalf("unexpected result size: %v", result) 1619 } 1620 1621 for i, r := range result { 1622 if r.Error != nil { 1623 t.Errorf("unexpected error: %s", r.Error) 1624 continue 1625 } 1626 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1627 t.Errorf("unexpected result: %v", r.Msg) 1628 } 1629 } 1630 }) 1631 1632 t.Run("retryable error and non-retryable errors", func(t *testing.T) { 1633 batch := make([]hrpc.Call, 9) 1634 for i := 0; i < 9; i++ { 1635 // Create an RPC for each region, "a"-"i" 1636 key := string(rune('a' + i)) 1637 batch[i] = newRPC(key) 1638 } 1639 var ( 1640 result []hrpc.RPCResult 1641 ok bool 1642 done = make(chan struct{}) 1643 ) 1644 go func() { 1645 result, ok = c.SendBatch(context.Background(), batch) 1646 close(done) 1647 }() 1648 1649 for i := 0; i < 9; i++ { 1650 if i%4 == 0 { 1651 // Non-retryable error for some 1652 batch[i].ResultChan() <- hrpc.RPCResult{Error: errors.New("error")} 1653 continue 1654 } 1655 // Retryable error for some 1656 batch[i].ResultChan() <- hrpc.RPCResult{Error: region.RetryableError{}} 1657 } 1658 1659 <-sleepCh // Expect one call to sleepAndIncreaseBackoff 1660 1661 // We should see retries on retryable errors. So now send the 1662 // correct response. 1663 for i := 0; i < 9; i++ { 1664 if i%4 == 0 { 1665 continue 1666 } 1667 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1668 } 1669 <-done 1670 if ok { 1671 t.Errorf("expected !ok") 1672 } 1673 if len(result) != 9 { 1674 t.Fatalf("unexpected result size: %v", result) 1675 } 1676 for i, r := range result { 1677 if i%4 == 0 { 1678 if r.Error == nil || r.Error.Error() != "error" { 1679 t.Errorf("expected error but got: %v", r.Error) 1680 } 1681 if r.Msg != nil { 1682 t.Errorf("unexpected Msg: %v", r.Msg) 1683 } 1684 continue 1685 } 1686 if r.Error != nil { 1687 t.Errorf("unexpected error: %s", r.Error) 1688 continue 1689 } 1690 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1691 t.Errorf("unexpected result: %v", r.Msg) 1692 } 1693 } 1694 }) 1695 1696 t.Run("not serving region error", func(t *testing.T) { 1697 batch := make([]hrpc.Call, 9) 1698 for i := 0; i < 9; i++ { 1699 // Create an RPC for each region, "a"-"i" 1700 key := string(rune('a' + i)) 1701 batch[i] = newRPC(key) 1702 } 1703 var ( 1704 result []hrpc.RPCResult 1705 ok bool 1706 done = make(chan struct{}) 1707 ) 1708 go func() { 1709 result, ok = c.SendBatch(context.Background(), batch) 1710 close(done) 1711 }() 1712 1713 for i := 0; i < 9; i++ { 1714 if i%4 == 0 { 1715 // NSRE for some 1716 batch[i].ResultChan() <- hrpc.RPCResult{Error: region.NotServingRegionError{}} 1717 continue 1718 } 1719 // success for some 1720 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1721 } 1722 1723 for i := 0; i < 9; i++ { 1724 if i%4 != 0 { 1725 continue 1726 } 1727 // For each failed NSRE we should expect an establishRegion call 1728 reg := <-estRegCh 1729 // reestablish the region: 1730 reg.MarkAvailable() 1731 } 1732 1733 // We should see retries on the RPCs hitting NSREs. So now 1734 // send the correct response. 1735 for i := 0; i < 9; i++ { 1736 if i%4 != 0 { 1737 continue 1738 } 1739 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1740 } 1741 <-done 1742 if !ok { 1743 t.Errorf("unexpected !ok") 1744 } 1745 if len(result) != 9 { 1746 t.Fatalf("unexpected result size: %v", result) 1747 } 1748 1749 for i, r := range result { 1750 if r.Error != nil { 1751 t.Errorf("unexpected error: %s", r.Error) 1752 continue 1753 } 1754 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1755 t.Errorf("unexpected result: %v", r.Msg) 1756 } 1757 } 1758 }) 1759 1760 t.Run("retryable error some with context cancellation", func(t *testing.T) { 1761 batch := make([]hrpc.Call, 9) 1762 for i := 0; i < 9; i++ { 1763 // Create an RPC for each region, "a"-"i" 1764 key := string(rune('a' + i)) 1765 batch[i] = newRPC(key) 1766 } 1767 var ( 1768 result []hrpc.RPCResult 1769 ok bool 1770 done = make(chan struct{}) 1771 ctx, cancel = context.WithCancel(context.Background()) 1772 ) 1773 go func() { 1774 result, ok = c.SendBatch(ctx, batch) 1775 close(done) 1776 }() 1777 1778 for i := 0; i < 9; i++ { 1779 // Error some responses 1780 if i%2 == 0 { 1781 batch[i].ResultChan() <- hrpc.RPCResult{Error: region.RetryableError{}} 1782 continue 1783 } 1784 // Using an Int32 as a result. A real result would be a 1785 // MutateResponse, but any proto.Message works for the test. 1786 batch[i].ResultChan() <- hrpc.RPCResult{Msg: wrapperspb.Int32(int32(i))} 1787 } 1788 1789 cancel() 1790 1791 <-done 1792 if ok { 1793 t.Errorf("unexpected ok") 1794 } 1795 if len(result) != 9 { 1796 t.Fatalf("unexpected result size: %v", result) 1797 } 1798 1799 for i, r := range result { 1800 if i%2 == 0 { 1801 if r.Error == nil || !errors.Is(r.Error, region.RetryableError{}) { 1802 t.Errorf("expected error but got: %v", r.Error) 1803 } 1804 if r.Msg != nil { 1805 t.Errorf("unexpected Msg: %v", r.Msg) 1806 } 1807 continue 1808 } 1809 if r.Error != nil { 1810 t.Errorf("unexpected error: %s", r.Error) 1811 continue 1812 } 1813 if r.Msg.(*wrapperspb.Int32Value).Value != int32(i) { 1814 t.Errorf("unexpected result: %v", r.Msg) 1815 } 1816 } 1817 }) 1818 } 1819 1820 // TestSendRPCStatsHandler tests control flow of the ScanStatsHandler being called when 1821 // SendRPC sends a ScanRequest 1822 func TestSendRPCStatsHandler(t *testing.T) { 1823 ctrl := test.NewController(t) 1824 defer ctrl.Finish() 1825 1826 establishRegionOverride = func(reg hrpc.RegionInfo, addr string) {} 1827 handleResultErrorOverride = func(err error, reg hrpc.RegionInfo, rc hrpc.RegionClient) {} 1828 1829 defer func() { 1830 establishRegionOverride = nil 1831 handleResultErrorOverride = nil 1832 }() 1833 1834 c := newMockClient(nil) 1835 rc := c.clients.put("regionserver:0", c.metaRegionInfo, 1836 newRegionClientFn("regionserver:0")) 1837 c.metaRegionInfo.SetClient(rc) 1838 1839 // Set up a region & regionserver 1840 expectedRegionID := uint64(1434573235910) 1841 ri := region.NewInfo(expectedRegionID, nil, []byte("test"), 1842 []byte("test,a,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("a"), []byte("z")) 1843 addr := "regionserver:0" 1844 regC := c.clients.put(addr, ri, newRegionClientFn(addr)) 1845 ri.SetClient(regC) 1846 overlaps, replaced := c.regions.put(ri) 1847 if len(overlaps) > 0 { 1848 t.Fatalf("overlaps: %v replaced: %t", overlaps, replaced) 1849 } 1850 1851 ss := &hrpc.ScanStats{} 1852 handlerCalledOnce := false 1853 h := func(stats *hrpc.ScanStats) { 1854 if !handlerCalledOnce { 1855 ss = stats 1856 t.Log(ss) 1857 handlerCalledOnce = true 1858 return 1859 } 1860 t.Log("Handler doing nothing, already called once for test case") 1861 } 1862 1863 t.Run("no handler called when not a scan request", func(t *testing.T) { 1864 get, err := hrpc.NewGet(context.Background(), []byte("test"), []byte("a")) 1865 if err != nil { 1866 t.Fatalf("Error creating get request: %v", err) 1867 } 1868 1869 go func() { 1870 res := hrpc.RPCResult{} 1871 get.ResultChan() <- res 1872 }() 1873 1874 _, err = c.SendRPC(get) 1875 if err != nil { 1876 t.Fatalf("Error sending RPC: %v", err) 1877 } 1878 1879 if !cmp.Equal(ss, &hrpc.ScanStats{}) || handlerCalledOnce { 1880 t.Fatalf("Scan stats should not have been updated for a non-scan request,"+ 1881 "ScanStats: %s", ss.String()) 1882 } 1883 }) 1884 1885 tcases := []struct { 1886 name string 1887 rpcErr error 1888 retryable bool 1889 }{ 1890 { 1891 name: "nil error", 1892 rpcErr: nil, 1893 retryable: false, 1894 }, 1895 { 1896 name: "RetryableError", 1897 rpcErr: region.RetryableError{}, 1898 retryable: true, 1899 }, 1900 { 1901 name: "ServerError", 1902 rpcErr: region.ServerError{}, 1903 retryable: true, 1904 }, 1905 { 1906 name: "NotServingRegionError", 1907 rpcErr: region.NotServingRegionError{}, 1908 retryable: true, 1909 }, 1910 { 1911 name: "Other error", 1912 rpcErr: errors.New("random error"), 1913 retryable: false, 1914 }, 1915 } 1916 1917 for _, tc := range tcases { 1918 t.Run(tc.name, func(t *testing.T) { 1919 handlerCalledOnce = false 1920 ss = &hrpc.ScanStats{} 1921 1922 expectedTable := []byte("test") 1923 expectedStartRow := []byte("a") 1924 expectedEndRow := []byte("z") 1925 scan, err := hrpc.NewScanRange( 1926 context.Background(), expectedTable, expectedStartRow, expectedEndRow, 1927 hrpc.WithScanStatsHandler(h)) 1928 if err != nil { 1929 t.Fatalf("Failed to create scan req: %v", err) 1930 } 1931 expectedScanStatsID := scan.ScanStatsID() 1932 scan.ResponseSize = 42 1933 expectedResponseSize := scan.ResponseSize 1934 1935 go func() { 1936 res := hrpc.RPCResult{ 1937 Error: tc.rpcErr, 1938 } 1939 scan.ResultChan() <- res 1940 // Workaround to not retry forever: if it's retryable, send a non error result after 1941 // to get out of retry loop. The ScanStats are only updated by the handler on the 1942 // first iteration of the for loop in SendRPC and that is what is being tested 1943 scan.ResultChan() <- hrpc.RPCResult{} 1944 }() 1945 1946 _, err = c.SendRPC(scan) 1947 1948 if tc.rpcErr == nil && err != nil { 1949 if ss.Error || ss.Retryable != tc.retryable { 1950 t.Fatalf("ScanStats incorrectly set error fields in nil error case") 1951 } 1952 } 1953 if tc.rpcErr != nil { 1954 if !ss.Error { 1955 t.Fatalf("ScanStats Error for %v not set as expected", tc.rpcErr) 1956 } 1957 if ss.Retryable != tc.retryable { 1958 t.Fatalf("ScanStats Retryable was not set as expected to %t for err %v", 1959 tc.retryable, tc.rpcErr) 1960 } 1961 } 1962 1963 if !bytes.Equal(ss.Table, expectedTable) || 1964 !bytes.Equal(ss.StartRow, expectedStartRow) || 1965 !bytes.Equal(ss.EndRow, expectedEndRow) || 1966 // The region & scanner info should remain constant since there is only one region 1967 // that can be scanned over 1968 ss.RegionID != expectedRegionID || 1969 ss.RegionServer != addr || 1970 ss.ScannerID != noScannerID || 1971 ss.ScanStatsID != expectedScanStatsID || 1972 ss.ResponseSize != expectedResponseSize { 1973 t.Fatalf("ScanStats not updated as expected, got: %v", ss) 1974 } 1975 }) 1976 } 1977 } 1978 1979 // TestScanRPCScanStatsScanMetricsNonScanResponse tests the helper that updates the ScanStats when 1980 // there's an unexpected response, either nil or a pb message that isn't the expected ScanResponse 1981 // type. This scenario could arise when sendBlocking returns an error, and along with it a nil rpc 1982 // result. In either case the handler can still update ScanStats, but when ScanMetrics is enabled, 1983 // it shouldn't be able to update it since there's no ScanResponse or ScanMetrics to do so. 1984 func TestScanRPCScanStatsScanMetricsNonScanResponse(t *testing.T) { 1985 c := newMockClient(nil) 1986 1987 ss := &hrpc.ScanStats{} 1988 h := func(stats *hrpc.ScanStats) { 1989 ss = stats 1990 t.Log(ss) 1991 } 1992 expectedTable := []byte("test") 1993 expectedStartRow := []byte("a") 1994 expectedEndRow := []byte("z") 1995 expectedRegionID := uint64(1434573235910) 1996 1997 scan, err := hrpc.NewScanRange( 1998 context.Background(), expectedTable, expectedStartRow, expectedEndRow, 1999 hrpc.WithScanStatsHandler(h), hrpc.TrackScanMetrics()) 2000 if err != nil { 2001 t.Fatalf("Failed to create scan req: %v", err) 2002 } 2003 ri := region.NewInfo(expectedRegionID, nil, []byte("test"), 2004 []byte("test,a,1434573235910.56f833d5569a27c7a43fbf547b4924a4."), []byte("a"), []byte("z")) 2005 addr := "regionserver:0" 2006 rc := newMockRegionClient(addr, region.RegionClient, 2007 0, 0, "root", region.DefaultReadTimeout, nil, nil, slog.Default()) 2008 ri.SetClient(rc) 2009 scan.SetRegion(ri) 2010 expectedScanStatsID := scan.ScanStatsID() 2011 start := time.Unix(0, 11) 2012 end := time.Unix(0, 77) 2013 2014 validateStats := func() { 2015 if !bytes.Equal(ss.Table, expectedTable) || 2016 !bytes.Equal(ss.StartRow, expectedStartRow) || 2017 !bytes.Equal(ss.EndRow, expectedEndRow) || 2018 ss.RegionID != expectedRegionID || 2019 ss.RegionServer != addr || 2020 ss.ScannerID != noScannerID || 2021 ss.ScanStatsID != expectedScanStatsID || 2022 ss.Start != start || ss.End != end { 2023 t.Fatalf("ScanStats not updated as expected, got: %v", ss) 2024 } 2025 } 2026 2027 tcases := []struct { 2028 name string 2029 err error 2030 }{ 2031 { 2032 name: "nil response with error (sendBlocking error case)", 2033 err: errors.New("err"), 2034 }, 2035 { 2036 name: "nil response no error", 2037 }, 2038 { 2039 name: "non scan response with error", 2040 err: errors.New("err"), 2041 }, 2042 { 2043 name: "non scan response no error", 2044 }, 2045 } 2046 for _, tc := range tcases { 2047 t.Run(tc.name, func(t *testing.T) { 2048 ss = &hrpc.ScanStats{} 2049 c.scanRpcScanStats(scan, nil, tc.err, false, start, end) 2050 validateStats() 2051 if ss.ScanMetrics != nil { 2052 t.Fatal("ScanStats set scanMetrics unexpectedly") 2053 } 2054 if tc.err != nil { 2055 if !ss.Error { 2056 t.Fatal("ScanStats did not set error as expected") 2057 } 2058 } else { 2059 if ss.Error { 2060 t.Fatal("ScanStats set error unexpectedly") 2061 } 2062 } 2063 if ss.Retryable { 2064 t.Fatal("ScanStats set retry unexpectedly") 2065 } 2066 }) 2067 } 2068 }