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  }