github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/region/client_test.go (about)

     1  // Copyright (C) 2015  The GoHBase Authors.  All rights reserved.
     2  // This file is part of GoHBase.
     3  // Use of this source code is governed by the Apache License 2.0
     4  // that can be found in the COPYING file.
     5  
     6  package region
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"encoding/binary"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"log/slog"
    16  	"math"
    17  	"net"
    18  	"reflect"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/tsuna/gohbase/hrpc"
    28  	"github.com/tsuna/gohbase/pb"
    29  	"github.com/tsuna/gohbase/test"
    30  	"github.com/tsuna/gohbase/test/mock"
    31  	"go.uber.org/mock/gomock"
    32  	"google.golang.org/protobuf/encoding/protowire"
    33  	"google.golang.org/protobuf/proto"
    34  )
    35  
    36  func TestErrors(t *testing.T) {
    37  	ue := ServerError{fmt.Errorf("oops")}
    38  	if ue.Error() != "region.ServerError: oops" {
    39  		t.Errorf("Wrong error message. Got %q, wanted %q", ue, "region.ServerError: oops")
    40  	}
    41  }
    42  
    43  func TestWrite(t *testing.T) {
    44  	ctrl := test.NewController(t)
    45  	defer ctrl.Finish()
    46  	mockConn := mock.NewMockConn(ctrl)
    47  	c := &client{
    48  		conn: mockConn,
    49  	}
    50  
    51  	// check if Write returns an error
    52  	expectErr := errors.New("nope")
    53  	mockConn.EXPECT().Write(gomock.Any()).Return(0, expectErr).Times(1)
    54  	err := c.write([]byte("lol"))
    55  	if err != expectErr {
    56  		t.Errorf("expected %v, got %v", expectErr, err)
    57  	}
    58  
    59  	// check if it actually writes the right data
    60  	expected := []byte("lol")
    61  	mockConn.EXPECT().Write(gomock.Any()).Return(3, nil).Times(1).Do(func(buf []byte) {
    62  		if !bytes.Equal(expected, buf) {
    63  			t.Errorf("expected %v, got %v", expected, buf)
    64  		}
    65  	})
    66  	err = c.write(expected)
    67  	if err != nil {
    68  		t.Errorf("Was expecting error, but got one: %#v", err)
    69  	}
    70  }
    71  
    72  func TestSendHello(t *testing.T) {
    73  	ctrl := test.NewController(t)
    74  	defer ctrl.Finish()
    75  	mockConn := mock.NewMockConn(ctrl)
    76  	c := &client{
    77  		conn:          mockConn,
    78  		effectiveUser: "root",
    79  		ctype:         RegionClient,
    80  	}
    81  
    82  	// check if it's sending the right "hello" for RegionClient
    83  	mockConn.EXPECT().Write(gomock.Any()).Return(78, nil).Times(1).Do(func(buf []byte) {
    84  		expected := []byte("HBas\x00P\x00\x00\x00D\n\x06\n\x04root\x12\rClientService\x1a+" +
    85  			"org.apache.hadoop.hbase.codec.KeyValueCodec")
    86  		if !bytes.Equal(expected, buf) {
    87  			t.Errorf("expected %v, got %v", expected, buf)
    88  		}
    89  	})
    90  	err := c.sendHello()
    91  	if err != nil {
    92  		t.Errorf("Wasn't expecting error, but got one: %#v", err)
    93  	}
    94  
    95  	// check if it sends the right "hello" for MasterClient
    96  	c.ctype = MasterClient
    97  	mockConn.EXPECT().Write(gomock.Any()).Return(78, nil).Times(1).Do(func(buf []byte) {
    98  		expected := []byte("HBas\x00P\x00\x00\x00D\n\x06\n\x04root\x12\rMasterService\x1a+" +
    99  			"org.apache.hadoop.hbase.codec.KeyValueCodec")
   100  		if !bytes.Equal(expected, buf) {
   101  			t.Errorf("expected %v, got %v", expected, buf)
   102  		}
   103  	})
   104  	err = c.sendHello()
   105  	if err != nil {
   106  		t.Errorf("Was expecting error, but got one: %#v", err)
   107  	}
   108  
   109  	c.compressor = &compressor{Codec: mockCodec{}}
   110  	// check if compressor is getting sent
   111  	c.ctype = RegionClient
   112  	mockConn.EXPECT().Write(gomock.Any()).Return(78, nil).Times(1).Do(func(buf []byte) {
   113  		expected := []byte("HBas\x00P\x00\x00\x00J\n\x06\n\x04root\x12\rClientService\x1a+" +
   114  			"org.apache.hadoop.hbase.codec.KeyValueCodec\"\x04mock")
   115  		if !bytes.Equal(expected, buf) {
   116  			t.Errorf("expected %q, got %q", expected, buf)
   117  		}
   118  	})
   119  	err = c.sendHello()
   120  	if err != nil {
   121  		t.Errorf("Was expecting error, but got one: %#v", err)
   122  	}
   123  }
   124  
   125  func TestFail(t *testing.T) {
   126  	ctrl := test.NewController(t)
   127  	defer ctrl.Finish()
   128  	mockConn := mock.NewMockConn(ctrl)
   129  	c := &client{
   130  		conn:   mockConn,
   131  		done:   make(chan struct{}),
   132  		rpcs:   make(chan []hrpc.Call),
   133  		sent:   make(map[uint32]hrpc.Call),
   134  		logger: slog.Default(),
   135  	}
   136  	expectedErr := errors.New("oooups")
   137  
   138  	// check that connection Close is called only once
   139  	mockConn.EXPECT().Close().Times(1)
   140  
   141  	// run multiple in parallel to make sure everything is called only once
   142  	var wg sync.WaitGroup
   143  	for i := 0; i < 100; i++ {
   144  		wg.Add(1)
   145  		go func() {
   146  			c.fail(expectedErr)
   147  			wg.Done()
   148  		}()
   149  	}
   150  	wg.Wait()
   151  
   152  	// check if done channel is closed to notify goroutines to stop
   153  	// if close(c.done) is called more than once, it would panic
   154  	select {
   155  	case <-time.After(2 * time.Second):
   156  		t.Errorf("done hasn't been closed")
   157  	case _, more := <-c.done:
   158  		if more {
   159  			t.Error("expected done to be closed")
   160  		}
   161  	}
   162  
   163  	// check that failing undialed client doesn't panic
   164  	c = &client{
   165  		done:   make(chan struct{}),
   166  		rpcs:   make(chan []hrpc.Call),
   167  		sent:   make(map[uint32]hrpc.Call),
   168  		logger: slog.Default(),
   169  	}
   170  	c.fail(expectedErr)
   171  }
   172  
   173  type mc struct {
   174  	*mock.MockConn
   175  
   176  	closed uint32
   177  }
   178  
   179  func (c *mc) Write(b []byte) (n int, err error) {
   180  	if v := atomic.LoadUint32(&c.closed); v != 0 {
   181  		return 0, errors.New("closed connection")
   182  	}
   183  	return 0, nil
   184  }
   185  
   186  func (c *mc) Close() error {
   187  	atomic.AddUint32(&c.closed, 1)
   188  	return nil
   189  }
   190  
   191  func TestQueueRPCMultiWithClose(t *testing.T) {
   192  	ctrl := test.NewController(t)
   193  	defer ctrl.Finish()
   194  
   195  	mockConn := mock.NewMockConn(ctrl)
   196  	mockConn.EXPECT().SetReadDeadline(gomock.Any()).AnyTimes()
   197  
   198  	ncalls := 1000
   199  
   200  	c := &client{
   201  		conn:          &mc{MockConn: mockConn},
   202  		rpcs:          make(chan []hrpc.Call),
   203  		done:          make(chan struct{}),
   204  		sent:          make(map[uint32]hrpc.Call),
   205  		rpcQueueSize:  10,
   206  		flushInterval: 30 * time.Millisecond,
   207  		logger:        slog.Default(),
   208  	}
   209  
   210  	var wgProcessRPCs sync.WaitGroup
   211  	wgProcessRPCs.Add(1)
   212  	go func() {
   213  		c.processRPCs()
   214  		wgProcessRPCs.Done()
   215  	}()
   216  
   217  	calls := make([]hrpc.Call, ncalls)
   218  	for i := range calls {
   219  		call, err := hrpc.NewGet(context.Background(), []byte("yolo"), []byte("swag"))
   220  		if err != nil {
   221  			t.Fatal(err)
   222  		}
   223  		call.SetRegion(reg0)
   224  		calls[i] = call
   225  	}
   226  
   227  	for _, call := range calls {
   228  		go c.QueueRPC(call)
   229  	}
   230  
   231  	c.Close()
   232  
   233  	// check that all calls are not stuck and get an error
   234  	for _, call := range calls {
   235  		r := <-call.ResultChan()
   236  		if r.Error == nil {
   237  			t.Error("got no error")
   238  		}
   239  	}
   240  
   241  	wgProcessRPCs.Wait()
   242  }
   243  
   244  type rpcMatcher struct {
   245  	payload []byte
   246  }
   247  
   248  func (m rpcMatcher) Matches(x interface{}) bool {
   249  	data, ok := x.([]byte)
   250  	if !ok {
   251  		return false
   252  	}
   253  	return bytes.HasSuffix(data, m.payload)
   254  }
   255  
   256  func (m rpcMatcher) String() string {
   257  	return fmt.Sprintf("RPC payload is equal to %q", m.payload)
   258  }
   259  
   260  func newRPCMatcher(payload []byte) gomock.Matcher {
   261  	return rpcMatcher{payload: payload}
   262  }
   263  
   264  func TestProcessRPCsWithFail(t *testing.T) {
   265  	ctrl := test.NewController(t)
   266  	defer ctrl.Finish()
   267  
   268  	mockConn := mock.NewMockConn(ctrl)
   269  	mockConn.EXPECT().Close()
   270  
   271  	ncalls := 100
   272  
   273  	c := &client{
   274  		conn: mockConn,
   275  		rpcs: make(chan []hrpc.Call),
   276  		done: make(chan struct{}),
   277  		sent: make(map[uint32]hrpc.Call),
   278  		// never send anything
   279  		rpcQueueSize:  ncalls + 1,
   280  		flushInterval: 1000 * time.Hour,
   281  		logger:        slog.Default(),
   282  	}
   283  
   284  	var wgProcessRPCs sync.WaitGroup
   285  	wgProcessRPCs.Add(1)
   286  	go func() {
   287  		c.processRPCs()
   288  		wgProcessRPCs.Done()
   289  	}()
   290  
   291  	calls := make([]hrpc.Call, ncalls)
   292  	for i := range calls {
   293  		call, err := hrpc.NewGet(context.Background(), []byte("yolo"), []byte("swag"))
   294  		if err != nil {
   295  			t.Fatal(err)
   296  		}
   297  		call.SetRegion(reg0)
   298  		calls[i] = call
   299  	}
   300  
   301  	for _, call := range calls {
   302  		c.QueueRPC(call)
   303  	}
   304  
   305  	c.fail(errors.New("OOOPS"))
   306  
   307  	// check that all calls are not stuck and get an error
   308  	for _, call := range calls {
   309  		r := <-call.ResultChan()
   310  		if r.Error != ErrClientClosed {
   311  			t.Errorf("got unexpected error %v, expected %v", r.Error, ErrClientClosed)
   312  		}
   313  	}
   314  
   315  	wgProcessRPCs.Wait()
   316  }
   317  
   318  func mockRPCProto(row string) (proto.Message, []byte) {
   319  	regionType := pb.RegionSpecifier_REGION_NAME
   320  	r := &pb.RegionSpecifier{
   321  		Type:  &regionType,
   322  		Value: []byte("yolo"),
   323  	}
   324  	get := &pb.GetRequest{Region: r, Get: &pb.Get{Row: []byte(row)}}
   325  
   326  	var b []byte
   327  	var err error
   328  	b = protowire.AppendVarint(b, uint64(proto.Size(get)))
   329  	b, err = proto.MarshalOptions{}.MarshalAppend(b, get)
   330  	if err != nil {
   331  		panic(err)
   332  	}
   333  	return get, b
   334  }
   335  
   336  func TestQueueRPC(t *testing.T) {
   337  	ctrl := test.NewController(t)
   338  	defer ctrl.Finish()
   339  
   340  	queueSize := 30
   341  	flushInterval := 20 * time.Millisecond
   342  	mockConn := mock.NewMockConn(ctrl)
   343  	mockConn.EXPECT().SetReadDeadline(gomock.Any()).AnyTimes()
   344  	c := &client{
   345  		conn:          mockConn,
   346  		rpcs:          make(chan []hrpc.Call),
   347  		done:          make(chan struct{}),
   348  		sent:          make(map[uint32]hrpc.Call),
   349  		rpcQueueSize:  queueSize,
   350  		flushInterval: flushInterval,
   351  		logger:        slog.Default(),
   352  	}
   353  	var wgProcessRPCs sync.WaitGroup
   354  	wgProcessRPCs.Add(1)
   355  	go func() {
   356  		c.processRPCs() // Writer goroutine
   357  		wgProcessRPCs.Done()
   358  	}()
   359  
   360  	// define rpcs behaviour
   361  	var wgWrites sync.WaitGroup
   362  	calls := make([]hrpc.Call, 100)
   363  	ctx := context.Background()
   364  	for i := range calls {
   365  		wgWrites.Add(1)
   366  		mockCall := mock.NewMockCall(ctrl)
   367  		mockCall.EXPECT().Name().Return("Get").Times(1)
   368  		p, payload := mockRPCProto(fmt.Sprintf("rpc_%d", i))
   369  		mockCall.EXPECT().ToProto().Return(p).Times(1)
   370  		mockCall.EXPECT().Context().Return(ctx).AnyTimes()
   371  		mockCall.EXPECT().Description().AnyTimes()
   372  		mockCall.EXPECT().ResultChan().Return(make(chan hrpc.RPCResult, 1)).Times(1)
   373  		calls[i] = mockCall
   374  
   375  		// we expect that it eventually writes to connection
   376  		mockConn.EXPECT().Write(newRPCMatcher(payload)).Times(1).Return(14+len(payload), nil).Do(
   377  			func(buf []byte) { wgWrites.Done() })
   378  	}
   379  
   380  	// queue calls in parallel
   381  	for _, call := range calls {
   382  		go func(call hrpc.Call) {
   383  			c.QueueRPC(call)
   384  		}(call)
   385  	}
   386  
   387  	// wait till all writes complete
   388  	wgWrites.Wait()
   389  
   390  	// check sent map
   391  	if len(c.sent) != len(calls) {
   392  		t.Errorf("expected len(c.sent) be %d, got %d", len(calls), len(c.sent))
   393  	}
   394  
   395  	var wg sync.WaitGroup
   396  	// now we fail the regionserver, and try to queue stuff
   397  	mockConn.EXPECT().Close().Times(1)
   398  	c.fail(errors.New("ooups"))
   399  	for i := 0; i < 100; i++ {
   400  		wg.Add(1)
   401  		go func() {
   402  			defer wg.Done()
   403  			result := make(chan hrpc.RPCResult, 1)
   404  			mockCall := mock.NewMockCall(ctrl)
   405  			mockCall.EXPECT().Context().Return(ctx).AnyTimes()
   406  			mockCall.EXPECT().Description().AnyTimes()
   407  			mockCall.EXPECT().ResultChan().Return(result).Times(1)
   408  			c.QueueRPC(mockCall)
   409  			r := <-result
   410  			err, ok := r.Error.(ServerError)
   411  			if !ok {
   412  				t.Errorf("Expected ServerError error")
   413  				return
   414  			}
   415  			if ErrClientClosed != err {
   416  				t.Errorf("expected %v, got %v", ErrClientClosed, err)
   417  			}
   418  		}()
   419  	}
   420  	wg.Wait()
   421  	wgProcessRPCs.Wait()
   422  }
   423  
   424  func TestServerErrorWrite(t *testing.T) {
   425  	ctrl := test.NewController(t)
   426  	defer ctrl.Finish()
   427  
   428  	queueSize := 1
   429  	flushInterval := 10 * time.Millisecond
   430  	mockConn := mock.NewMockConn(ctrl)
   431  	c := &client{
   432  		conn:          mockConn,
   433  		rpcs:          make(chan []hrpc.Call),
   434  		done:          make(chan struct{}),
   435  		sent:          make(map[uint32]hrpc.Call),
   436  		rpcQueueSize:  queueSize,
   437  		flushInterval: flushInterval,
   438  		logger:        slog.Default(),
   439  	}
   440  	// define rpcs behaviour
   441  	mockCall := mock.NewMockCall(ctrl)
   442  	p, payload := mockRPCProto("rpc")
   443  	mockCall.EXPECT().ToProto().Return(p).Times(1)
   444  	mockCall.EXPECT().Name().Return("Get").Times(1)
   445  	mockCall.EXPECT().Context().Return(context.Background()).AnyTimes()
   446  	mockCall.EXPECT().Description().AnyTimes()
   447  	result := make(chan hrpc.RPCResult, 1)
   448  	mockCall.EXPECT().ResultChan().Return(result).Times(1)
   449  	// we expect that it eventually writes to connection
   450  	expErr := errors.New("Write failure")
   451  	mockConn.EXPECT().Write(newRPCMatcher(payload)).Times(1).Return(0, expErr)
   452  	mockConn.EXPECT().Close()
   453  
   454  	c.QueueRPC(mockCall)
   455  	// check that processRPCs exists
   456  	c.processRPCs()
   457  	r := <-result
   458  	if ErrClientClosed != r.Error {
   459  		t.Errorf("expected %v, got %v", ErrClientClosed, r.Error)
   460  	}
   461  	if len(c.sent) != 0 {
   462  		t.Errorf("Expected all awaiting rpcs to be processed, %d left", len(c.sent))
   463  	}
   464  }
   465  
   466  func TestServerErrorRead(t *testing.T) {
   467  	ctrl := test.NewController(t)
   468  	defer ctrl.Finish()
   469  
   470  	queueSize := 1
   471  	flushInterval := 10 * time.Millisecond
   472  	mockConn := mock.NewMockConn(ctrl)
   473  	c := &client{
   474  		conn:          mockConn,
   475  		rpcs:          make(chan []hrpc.Call),
   476  		done:          make(chan struct{}),
   477  		sent:          make(map[uint32]hrpc.Call),
   478  		rpcQueueSize:  queueSize,
   479  		flushInterval: flushInterval,
   480  		logger:        slog.Default(),
   481  	}
   482  	// define rpcs behavior
   483  	mockCall := mock.NewMockCall(ctrl)
   484  	result := make(chan hrpc.RPCResult, 1)
   485  	mockCall.EXPECT().ResultChan().Return(result).Times(1)
   486  	mockConn.EXPECT().Read(gomock.Any()).Return(0, errors.New("read failure"))
   487  	mockConn.EXPECT().Close()
   488  
   489  	// pretend we already unqueued and sent the rpc
   490  	c.sent[1] = mockCall
   491  	// now try receiving result, should call fail
   492  	c.receiveRPCs()
   493  	_, more := <-c.done
   494  	if more {
   495  		t.Error("expected done to be closed")
   496  	}
   497  	// finish reading from c.rpc to clean up the c.sent map
   498  	c.processRPCs()
   499  	if len(c.sent) != 0 {
   500  		t.Errorf("Expected all awaiting rpcs to be processed, %d left", len(c.sent))
   501  	}
   502  	r := <-result
   503  	if ErrClientClosed != r.Error {
   504  		t.Errorf("expected %v, got %v", ErrClientClosed, r.Error)
   505  	}
   506  }
   507  
   508  func TestServerErrorExceptionResponse(t *testing.T) {
   509  	ctrl := test.NewController(t)
   510  	defer ctrl.Finish()
   511  	mockConn := mock.NewMockConn(ctrl)
   512  	mockConn.EXPECT().SetReadDeadline(gomock.Any()).Times(2)
   513  	c := &client{
   514  		conn:          mockConn,
   515  		rpcs:          make(chan []hrpc.Call),
   516  		done:          make(chan struct{}),
   517  		sent:          make(map[uint32]hrpc.Call),
   518  		rpcQueueSize:  1,
   519  		flushInterval: 1000 * time.Second,
   520  	}
   521  
   522  	rpc, err := hrpc.NewGetStr(context.Background(), "test1", "yolo")
   523  	if err != nil {
   524  		t.Fatalf("Failed to create Get request: %s", err)
   525  	}
   526  
   527  	c.registerRPC(rpc)
   528  	if err := c.inFlightUp(); err != nil {
   529  		t.Fatal(err)
   530  	}
   531  
   532  	var response []byte
   533  	header := &pb.ResponseHeader{
   534  		CallId: proto.Uint32(1),
   535  		Exception: &pb.ExceptionResponse{
   536  			ExceptionClassName: proto.String(
   537  				"org.apache.hadoop.hbase.regionserver.RegionServerAbortedException"),
   538  			StackTrace: proto.String("ooops"),
   539  		},
   540  	}
   541  
   542  	response = protowire.AppendVarint(response, uint64(proto.Size(header)))
   543  	response, err = proto.MarshalOptions{}.MarshalAppend(response, header)
   544  	if err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	mockConn.EXPECT().Read(readBufSizeMatcher{l: 4}).Times(1).Return(4, nil).
   549  		Do(func(buf []byte) { binary.BigEndian.PutUint32(buf, uint32(len(response))) })
   550  
   551  	mockConn.EXPECT().Read(readBufSizeMatcher{l: len(response)}).Times(1).
   552  		Return(len(response), nil).Do(func(buf []byte) { copy(buf, response) })
   553  
   554  	expErr := exceptionToError(
   555  		"org.apache.hadoop.hbase.regionserver.RegionServerAbortedException", "ooops")
   556  
   557  	err = c.receive(mockConn)
   558  	if _, ok := err.(ServerError); !ok {
   559  		if err.Error() != expErr.Error() {
   560  			t.Fatalf("expected ServerError with message %q, got %T: %v", expErr, err, err)
   561  		}
   562  	}
   563  
   564  	re := <-rpc.ResultChan()
   565  	if re.Error != err {
   566  		t.Errorf("expected error %v, got %v", err, re.Error)
   567  	}
   568  }
   569  
   570  func TestExceptionToError(t *testing.T) {
   571  	tcases := []struct {
   572  		class string
   573  		stack string
   574  		out   error
   575  	}{
   576  		{
   577  			class: "java.io.IOException",
   578  			stack: "ooops",
   579  			out:   errors.New("HBase Java exception java.io.IOException:\nooops"),
   580  		},
   581  		{
   582  			class: "java.io.IOException",
   583  			stack: "Cannot append; log is closed\nblahblah",
   584  			out: NotServingRegionError{errors.New("HBase Java exception java.io.IOException:\n" +
   585  				"Cannot append; log is closed\nblahblah")},
   586  		},
   587  		{
   588  			class: "org.apache.hadoop.hbase.CallQueueTooBigException",
   589  			stack: "blahblah",
   590  			out: RetryableError{errors.New(
   591  				"HBase Java exception org.apache.hadoop.hbase.CallQueueTooBigException:\n" +
   592  					"blahblah")},
   593  		},
   594  	}
   595  	for _, tcase := range tcases {
   596  		t.Run(tcase.class, func(t *testing.T) {
   597  			err := exceptionToError(tcase.class, tcase.stack)
   598  			if !reflect.DeepEqual(err, tcase.out) {
   599  				t.Fatalf("expected error %q, got error %q", tcase.out, err)
   600  			}
   601  		})
   602  	}
   603  }
   604  
   605  func TestReceiveDecodeProtobufError(t *testing.T) {
   606  	ctrl := test.NewController(t)
   607  	defer ctrl.Finish()
   608  
   609  	mockConn := mock.NewMockConn(ctrl)
   610  	c := &client{
   611  		conn: mockConn,
   612  		done: make(chan struct{}),
   613  		sent: make(map[uint32]hrpc.Call),
   614  	}
   615  
   616  	mockCall := mock.NewMockCall(ctrl)
   617  	result := make(chan hrpc.RPCResult, 1)
   618  	mockCall.EXPECT().ResultChan().Return(result).Times(1)
   619  	mockCall.EXPECT().NewResponse().Return(&pb.MutateResponse{}).Times(1)
   620  	mockCall.EXPECT().Context().Return(context.Background()).Times(1)
   621  
   622  	c.sent[1] = mockCall
   623  	c.inFlight = 1
   624  
   625  	// Append mutate response with a chunk in the middle missing
   626  	response := []byte{6, 8, 1, 26, 2, 8, 38, 34, 0, 0, 0, 22,
   627  		0, 0, 0, 4, 0, 4, 121, 111, 108, 111, 2, 99, 102, 115, 119, 97, 103, 0, 0, 0, 0, 0, 0,
   628  		0, 0, 4, 109, 101, 111, 119}
   629  	mockConn.EXPECT().Read(readBufSizeMatcher{l: 4}).Times(1).Return(4, nil).
   630  		Do(func(buf []byte) { binary.BigEndian.PutUint32(buf, uint32(len(response))) })
   631  	mockConn.EXPECT().Read(readBufSizeMatcher{l: len(response)}).Times(1).
   632  		Return(len(response), nil).Do(func(buf []byte) { copy(buf, response) })
   633  	mockConn.EXPECT().SetReadDeadline(time.Time{}).Times(1)
   634  	expErrorPefix := "region.RetryableError: failed to decode the response: proto:"
   635  
   636  	err := c.receive(mockConn)
   637  	if err == nil || strings.HasPrefix(expErrorPefix, err.Error()) {
   638  		t.Errorf("Expected error prefix %v, got %v", expErrorPefix, err)
   639  	}
   640  
   641  	res := <-result
   642  	if err != res.Error {
   643  		t.Errorf("Expected error %v, got %v", err, res.Error)
   644  	}
   645  }
   646  
   647  type callWithCellBlocksError struct{ hrpc.Call }
   648  
   649  func (cwcbe callWithCellBlocksError) DeserializeCellBlocks(proto.Message, []byte) (uint32, error) {
   650  	return 0, errors.New("OOPS")
   651  }
   652  
   653  func TestReceiveDeserializeCellblocksError(t *testing.T) {
   654  	ctrl := test.NewController(t)
   655  	defer ctrl.Finish()
   656  
   657  	mockConn := mock.NewMockConn(ctrl)
   658  	c := &client{
   659  		conn: mockConn,
   660  		done: make(chan struct{}),
   661  		sent: make(map[uint32]hrpc.Call),
   662  	}
   663  
   664  	mockCall := mock.NewMockCall(ctrl)
   665  	result := make(chan hrpc.RPCResult, 1)
   666  	mockCall.EXPECT().ResultChan().Return(result).Times(1)
   667  	mockCall.EXPECT().NewResponse().Return(&pb.MutateResponse{}).Times(1)
   668  	mockCall.EXPECT().Context().Return(context.Background()).Times(1)
   669  
   670  	c.sent[1] = callWithCellBlocksError{mockCall}
   671  	c.inFlight = 1
   672  
   673  	// Append mutate response
   674  	response := []byte{6, 8, 1, 26, 2, 8, 38, 6, 10, 4, 16, 1, 32, 0, 0, 0, 0, 34, 0, 0, 0, 22,
   675  		0, 0, 0, 4, 0, 4, 121, 111, 108, 111, 2, 99, 102, 115, 119, 97, 103, 0, 0, 0, 0, 0, 0,
   676  		0, 0, 4, 109, 101, 111, 119}
   677  	mockConn.EXPECT().Read(readBufSizeMatcher{l: 4}).Times(1).Return(4, nil).
   678  		Do(func(buf []byte) { binary.BigEndian.PutUint32(buf, uint32(len(response))) })
   679  	mockConn.EXPECT().Read(readBufSizeMatcher{l: len(response)}).Times(1).
   680  		Return(len(response), nil).Do(func(buf []byte) { copy(buf, response) })
   681  	mockConn.EXPECT().SetReadDeadline(time.Time{}).Times(1)
   682  	expError := errors.New("region.RetryableError: failed to decode the response: OOPS")
   683  
   684  	err := c.receive(mockConn)
   685  	if err == nil || err.Error() != expError.Error() {
   686  		t.Errorf("Expected error %v, got %v", expError, err)
   687  	}
   688  
   689  	res := <-result
   690  	if err != res.Error {
   691  		t.Errorf("Expected error %v, got %v", err, res.Error)
   692  	}
   693  }
   694  
   695  func TestUnexpectedSendError(t *testing.T) {
   696  	ctrl := test.NewController(t)
   697  	defer ctrl.Finish()
   698  
   699  	queueSize := 1
   700  	flushInterval := 10 * time.Millisecond
   701  	mockConn := mock.NewMockConn(ctrl)
   702  	c := &client{
   703  		conn:          mockConn,
   704  		rpcs:          make(chan []hrpc.Call),
   705  		done:          make(chan struct{}),
   706  		sent:          make(map[uint32]hrpc.Call),
   707  		rpcQueueSize:  queueSize,
   708  		flushInterval: flushInterval,
   709  		logger:        slog.Default(),
   710  	}
   711  	go c.processRPCs()
   712  	// define rpcs behaviour
   713  	mockCall := mock.NewMockCall(ctrl)
   714  	mockCall.EXPECT().ToProto().Return(nil).Times(1)
   715  	mockCall.EXPECT().Context().Return(context.Background()).AnyTimes()
   716  	mockCall.EXPECT().Description().AnyTimes()
   717  	result := make(chan hrpc.RPCResult, 1)
   718  	mockCall.EXPECT().ResultChan().Return(result).Times(1)
   719  	mockCall.EXPECT().Name().Return("Whatever").Times(1)
   720  
   721  	c.QueueRPC(mockCall)
   722  	r := <-result
   723  	expectedErr := "failed to marshal request: proto: Marshal called with nil"
   724  	if err := r.Error; err == nil || err.Error() != expectedErr {
   725  		t.Errorf("expected %q, got %v", expectedErr, err)
   726  	}
   727  	if len(c.sent) != 0 {
   728  		t.Errorf("Expected all awaiting rpcs to be processed, %d left", len(c.sent))
   729  	}
   730  	// stop the go routine
   731  	mockConn.EXPECT().Close()
   732  	c.Close()
   733  }
   734  
   735  func TestProcessRPCs(t *testing.T) {
   736  	ctrl := test.NewController(t)
   737  	defer ctrl.Finish()
   738  
   739  	tests := []struct {
   740  		qsize      int
   741  		interval   time.Duration
   742  		ncalls     int
   743  		minsent    int
   744  		maxsent    int
   745  		concurrent bool
   746  	}{
   747  		{qsize: 100000, interval: 30 * time.Millisecond, ncalls: 100,
   748  			minsent: 1, maxsent: 1},
   749  		{qsize: 2, interval: 1000 * time.Hour, ncalls: 100, minsent: 50, maxsent: 50},
   750  		{qsize: 100, interval: 0 * time.Millisecond, ncalls: 1000,
   751  			minsent: 1, maxsent: 1000, concurrent: true},
   752  	}
   753  
   754  	for i, tcase := range tests {
   755  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   756  			mockConn := mock.NewMockConn(ctrl)
   757  			mockConn.EXPECT().Close()
   758  
   759  			c := &client{
   760  				conn:          mockConn,
   761  				rpcs:          make(chan []hrpc.Call),
   762  				done:          make(chan struct{}),
   763  				sent:          make(map[uint32]hrpc.Call),
   764  				rpcQueueSize:  tcase.qsize,
   765  				flushInterval: tcase.interval,
   766  				logger:        slog.Default(),
   767  			}
   768  
   769  			var wgProcessRPCs sync.WaitGroup
   770  			wgProcessRPCs.Add(1)
   771  			go func() {
   772  				c.processRPCs()
   773  				wgProcessRPCs.Done()
   774  			}()
   775  
   776  			var sent int32
   777  			var wgWrite sync.WaitGroup
   778  			wgWrite.Add(tcase.minsent)
   779  			mockConn.EXPECT().Write(gomock.Any()).MinTimes(tcase.minsent).
   780  				MaxTimes(tcase.maxsent).Return(42, nil).Do(func(buf []byte) {
   781  				if atomic.AddInt32(&sent, 1) <= int32(tcase.minsent) {
   782  					wgWrite.Done() // test will timeout if didn't get at least minsent
   783  				}
   784  			})
   785  			mockConn.EXPECT().SetReadDeadline(gomock.Any()).
   786  				MinTimes(tcase.minsent).MaxTimes(tcase.maxsent)
   787  
   788  			calls := make([]hrpc.Call, tcase.ncalls)
   789  			for i := range calls {
   790  				call, err := hrpc.NewGet(context.Background(), []byte("yolo"), []byte("swag"))
   791  				if err != nil {
   792  					t.Fatal(err)
   793  				}
   794  				call.SetRegion(reg0)
   795  				calls[i] = call
   796  			}
   797  
   798  			if tcase.concurrent {
   799  				var wg sync.WaitGroup
   800  				for _, call := range calls {
   801  					wg.Add(1)
   802  					go func(call hrpc.Call) {
   803  						c.QueueRPC(call)
   804  						wg.Done()
   805  					}(call)
   806  				}
   807  				wg.Wait()
   808  			} else {
   809  				for _, call := range calls {
   810  					c.QueueRPC(call)
   811  				}
   812  			}
   813  
   814  			wgWrite.Wait()
   815  
   816  			c.Close()
   817  			wgProcessRPCs.Wait()
   818  			t.Log("num batches sent", sent)
   819  
   820  			if f := int(c.inFlight); f < tcase.minsent || f > tcase.maxsent {
   821  				t.Errorf("expected [%d:%d] in-flight rpcs, got %d",
   822  					tcase.minsent, tcase.maxsent, c.inFlight)
   823  			}
   824  		})
   825  	}
   826  }
   827  
   828  func TestRPCContext(t *testing.T) {
   829  	ctrl := test.NewController(t)
   830  	defer ctrl.Finish()
   831  	queueSize := 10
   832  	flushInterval := 1000 * time.Second
   833  	mockConn := mock.NewMockConn(ctrl)
   834  	mockConn.EXPECT().Close()
   835  	c := &client{
   836  		conn:          mockConn,
   837  		rpcs:          make(chan []hrpc.Call),
   838  		done:          make(chan struct{}),
   839  		sent:          make(map[uint32]hrpc.Call),
   840  		rpcQueueSize:  queueSize,
   841  		flushInterval: flushInterval,
   842  		logger:        slog.Default(),
   843  	}
   844  
   845  	mockConn.EXPECT().SetReadDeadline(gomock.Any()).Times(1)
   846  
   847  	// queue rpc with background context
   848  	mockCall := mock.NewMockCall(ctrl)
   849  	mockCall.EXPECT().Name().Return("Get").Times(1)
   850  	p, payload := mockRPCProto("yolo")
   851  	mockCall.EXPECT().ToProto().Return(p).Times(1)
   852  	mockCall.EXPECT().Context().Return(context.Background()).AnyTimes()
   853  	mockCall.EXPECT().Description().AnyTimes()
   854  	mockCall.EXPECT().ResultChan().Return(make(chan hrpc.RPCResult, 1)).Times(1)
   855  	mockConn.EXPECT().Write(newRPCMatcher(payload)).Times(1).Return(14+len(payload), nil)
   856  	c.QueueRPC(mockCall)
   857  
   858  	ctxCancel, cancel := context.WithCancel(context.Background())
   859  	cancel()
   860  	callWithCancel := mock.NewMockCall(ctrl)
   861  	callWithCancel.EXPECT().Context().Return(ctxCancel).AnyTimes()
   862  	mockCall.EXPECT().Description().AnyTimes()
   863  	// this shouldn't block
   864  	c.QueueRPC(callWithCancel)
   865  
   866  	if int(c.inFlight) != 1 {
   867  		t.Errorf("expected %d in-flight rpcs, got %d", 1, c.inFlight)
   868  	}
   869  	// clean up
   870  	c.Close()
   871  }
   872  
   873  type readBufSizeMatcher struct {
   874  	l int
   875  }
   876  
   877  func (r readBufSizeMatcher) Matches(x interface{}) bool {
   878  	data, ok := x.([]byte)
   879  	if !ok {
   880  		return false
   881  	}
   882  	return len(data) == r.l
   883  }
   884  
   885  func (r readBufSizeMatcher) String() string {
   886  	return fmt.Sprintf("buf size is equal to %q", r.l)
   887  }
   888  
   889  func TestSanity(t *testing.T) {
   890  	ctrl := test.NewController(t)
   891  	defer ctrl.Finish()
   892  	mockConn := mock.NewMockConn(ctrl)
   893  	c := &client{
   894  		conn:          mockConn,
   895  		rpcs:          make(chan []hrpc.Call),
   896  		done:          make(chan struct{}),
   897  		sent:          make(map[uint32]hrpc.Call),
   898  		rpcQueueSize:  1, // size one to skip sendBatch
   899  		flushInterval: 1000 * time.Second,
   900  		logger:        slog.Default(),
   901  	}
   902  
   903  	var wg sync.WaitGroup
   904  
   905  	wg.Add(1)
   906  	go func() {
   907  		c.processRPCs()
   908  		wg.Done()
   909  	}()
   910  
   911  	// using Append as it returns cellblocks
   912  	app, err := hrpc.NewAppStr(context.Background(), "test1", "yolo",
   913  		map[string]map[string][]byte{"cf": map[string][]byte{"swag": []byte("meow")}})
   914  	if err != nil {
   915  		t.Fatalf("Failed to create Get request: %s", err)
   916  	}
   917  	app.SetRegion(
   918  		NewInfo(0, nil, []byte("test1"), []byte("test1,,lololololololololololo"), nil, nil))
   919  
   920  	mockConn.EXPECT().Write(gomock.Any()).Times(2).Return(0, nil)
   921  	mockConn.EXPECT().SetReadDeadline(gomock.Any()).Times(1)
   922  
   923  	c.QueueRPC(app)
   924  
   925  	response := []byte{6, 8, 1, 26, 2, 8, 38, 6, 10, 4, 16, 1, 32, 0, 0, 0, 0, 34, 0, 0, 0, 22,
   926  		0, 0, 0, 4, 0, 4, 121, 111, 108, 111, 2, 99, 102, 115, 119, 97, 103, 0, 0, 0, 0, 0, 0,
   927  		0, 0, 4, 109, 101, 111, 119}
   928  	mockConn.EXPECT().Read(gomock.Any()).Times(1).Return(4, nil).
   929  		Do(func(buf []byte) {
   930  			binary.BigEndian.PutUint32(buf, uint32(len(response)))
   931  		})
   932  	mockConn.EXPECT().Read(gomock.Any()).Times(1).
   933  		Return(len(response), nil).Do(func(buf []byte) {
   934  		copy(buf, response)
   935  
   936  		// stall the next read
   937  		mockConn.EXPECT().Read(gomock.Any()).MaxTimes(1).
   938  			Return(0, errors.New("closed")).Do(func(buf []byte) { <-c.done })
   939  	})
   940  	mockConn.EXPECT().SetReadDeadline(time.Time{}).Times(1)
   941  	wg.Add(1)
   942  	go func() {
   943  		c.receiveRPCs()
   944  		wg.Done()
   945  	}()
   946  
   947  	re := <-app.ResultChan()
   948  	if re.Error != nil {
   949  		t.Error(re.Error)
   950  	}
   951  	r, ok := re.Msg.(*pb.MutateResponse)
   952  	if !ok {
   953  		t.Fatalf("got unexpected type %T for response", r)
   954  	}
   955  	expResult := &pb.Result{
   956  		AssociatedCellCount: proto.Int32(1),
   957  		Stale:               proto.Bool(false),
   958  		Cell: []*pb.Cell{
   959  			&pb.Cell{
   960  				Row:       []byte("yolo"),
   961  				Family:    []byte("cf"),
   962  				Qualifier: []byte("swag"),
   963  				Value:     []byte("meow"),
   964  				CellType:  pb.CellType_PUT.Enum(),
   965  				Timestamp: proto.Uint64(0),
   966  			},
   967  		},
   968  	}
   969  	if !proto.Equal(expResult, r.Result) {
   970  		t.Errorf("expected %v, got %v", expResult, r.Result)
   971  	}
   972  	if int(c.inFlight) != 0 {
   973  		t.Errorf("expected %d in-flight rpcs, got %d", 0, c.inFlight)
   974  	}
   975  
   976  	mockConn.EXPECT().Close().Times(1)
   977  	c.Close()
   978  	wg.Wait()
   979  }
   980  
   981  func TestSanityCompressor(t *testing.T) {
   982  	ctrl := test.NewController(t)
   983  	defer ctrl.Finish()
   984  	mockConn := mock.NewMockConn(ctrl)
   985  	c := &client{
   986  		conn:          mockConn,
   987  		rpcs:          make(chan []hrpc.Call),
   988  		done:          make(chan struct{}),
   989  		sent:          make(map[uint32]hrpc.Call),
   990  		rpcQueueSize:  1, // size one to skip sendBatch
   991  		flushInterval: 1000 * time.Second,
   992  		compressor:    &compressor{Codec: mockCodec{}},
   993  		logger:        slog.Default(),
   994  	}
   995  
   996  	var wg sync.WaitGroup
   997  
   998  	wg.Add(1)
   999  	go func() {
  1000  		c.processRPCs()
  1001  		wg.Done()
  1002  	}()
  1003  
  1004  	// using Append as it returns cellblocks
  1005  	app, err := hrpc.NewAppStr(context.Background(), "test1", "yolo",
  1006  		map[string]map[string][]byte{"cf": map[string][]byte{"swag": []byte("meow")}})
  1007  	if err != nil {
  1008  		t.Fatalf("Failed to create Get request: %s", err)
  1009  	}
  1010  	app.SetRegion(
  1011  		NewInfo(0, nil, []byte("test1"), []byte("test1,,lololololololololololo"), nil, nil))
  1012  
  1013  	compressedCellblocks := "\x00\x00\x00&" +
  1014  		"\x00\x00\x00\n\x00\x00\x00\"\x00\x00\x00\x16\x00\x00" +
  1015  		"\x00\x00\x00\n\x00\x04\x00\x04yolo\x02c" +
  1016  		"\x00\x00\x00\nfswag\u007f\xff\xff\xff\xff" +
  1017  		"\x00\x00\x00\b\xff\xff\xff\x04meow"
  1018  
  1019  	mockConn.EXPECT().Write([]byte("\x00\x00\x00}\x10\b\x01\x1a\x06Mutate \x01*\x02\b:1\n!"+
  1020  		"\b\x01\x12\x1dtest1,,lololololololololololo\x12\f\n\x04yolo\x10\x000\x00@\x01")).Return(
  1021  		71, nil)
  1022  	mockConn.EXPECT().Write([]byte(compressedCellblocks)).Return(58, nil)
  1023  	mockConn.EXPECT().SetReadDeadline(gomock.Any())
  1024  
  1025  	c.QueueRPC(app)
  1026  
  1027  	header := &pb.ResponseHeader{
  1028  		CallId: proto.Uint32(1),
  1029  		CellBlockMeta: &pb.CellBlockMeta{
  1030  			Length: proto.Uint32(58),
  1031  		},
  1032  	}
  1033  
  1034  	mutate := &pb.MutateResponse{
  1035  		Result: &pb.Result{
  1036  			AssociatedCellCount: proto.Int32(1),
  1037  			Stale:               proto.Bool(false),
  1038  		},
  1039  	}
  1040  
  1041  	var response []byte
  1042  	response = protowire.AppendVarint(response, uint64(proto.Size(header)))
  1043  	response, err = proto.MarshalOptions{}.MarshalAppend(response, header)
  1044  	if err != nil {
  1045  		t.Fatal(err)
  1046  	}
  1047  	response = protowire.AppendVarint(response, uint64(proto.Size(mutate)))
  1048  	response, err = proto.MarshalOptions{}.MarshalAppend(response, mutate)
  1049  	if err != nil {
  1050  		t.Fatal(err)
  1051  	}
  1052  
  1053  	response = append(response, []byte(compressedCellblocks)...)
  1054  	mockConn.EXPECT().Read(gomock.Any()).Times(1).Return(4, nil).
  1055  		Do(func(buf []byte) {
  1056  			binary.BigEndian.PutUint32(buf, uint32(len(response)))
  1057  		})
  1058  	mockConn.EXPECT().Read(gomock.Any()).Times(1).
  1059  		Return(len(response), nil).Do(func(buf []byte) {
  1060  		copy(buf, response)
  1061  
  1062  		// stall the next read
  1063  		mockConn.EXPECT().Read(gomock.Any()).MaxTimes(1).
  1064  			Return(0, errors.New("closed")).Do(func(buf []byte) { <-c.done })
  1065  	})
  1066  	mockConn.EXPECT().SetReadDeadline(time.Time{}).Times(1)
  1067  	wg.Add(1)
  1068  	go func() {
  1069  		c.receiveRPCs()
  1070  		wg.Done()
  1071  	}()
  1072  
  1073  	re := <-app.ResultChan()
  1074  	if re.Error != nil {
  1075  		t.Error(re.Error)
  1076  	}
  1077  	r, ok := re.Msg.(*pb.MutateResponse)
  1078  	if !ok {
  1079  		t.Fatalf("got unexpected type %T for response", r)
  1080  	}
  1081  	expResult := &pb.Result{
  1082  		AssociatedCellCount: proto.Int32(1),
  1083  		Stale:               proto.Bool(false),
  1084  		Cell: []*pb.Cell{
  1085  			&pb.Cell{
  1086  				Row:       []byte("yolo"),
  1087  				Family:    []byte("cf"),
  1088  				Qualifier: []byte("swag"),
  1089  				Value:     []byte("meow"),
  1090  				CellType:  pb.CellType_PUT.Enum(),
  1091  				Timestamp: proto.Uint64(math.MaxInt64),
  1092  			},
  1093  		},
  1094  	}
  1095  	if !proto.Equal(expResult, r.Result) {
  1096  		t.Errorf("expected %v, got %v", expResult, r.Result)
  1097  	}
  1098  	if int(c.inFlight) != 0 {
  1099  		t.Errorf("expected %d in-flight rpcs, got %d", 0, c.inFlight)
  1100  	}
  1101  
  1102  	mockConn.EXPECT().Close().Times(1)
  1103  	c.Close()
  1104  	wg.Wait()
  1105  }
  1106  
  1107  func BenchmarkSendBatchMemory(b *testing.B) {
  1108  	ctrl := test.NewController(b)
  1109  	defer ctrl.Finish()
  1110  	mockConn := mock.NewMockConn(ctrl)
  1111  	c := &client{
  1112  		conn: mockConn,
  1113  		rpcs: make(chan []hrpc.Call),
  1114  		done: make(chan struct{}),
  1115  		sent: make(map[uint32]hrpc.Call),
  1116  		// queue size is 1 so that all QueueRPC calls trigger sendBatch,
  1117  		// and buffer slice reset
  1118  		rpcQueueSize:  1,
  1119  		flushInterval: 1000 * time.Second,
  1120  		logger:        slog.Default(),
  1121  	}
  1122  
  1123  	var wgWrites sync.WaitGroup
  1124  	ctx := context.Background()
  1125  	mockCall := mock.NewMockCall(ctrl)
  1126  	mockCall.EXPECT().Name().Return("Get").AnyTimes()
  1127  	p, _ := mockRPCProto("rpc")
  1128  	mockCall.EXPECT().ToProto().Return(p).AnyTimes()
  1129  	mockCall.EXPECT().Context().Return(ctx).AnyTimes()
  1130  	mockConn.EXPECT().Write(gomock.Any()).AnyTimes().Return(0, nil).Do(func(buf []byte) {
  1131  		wgWrites.Done()
  1132  	})
  1133  	mockConn.EXPECT().SetReadDeadline(gomock.Any()).AnyTimes()
  1134  
  1135  	go c.processRPCs()
  1136  	b.ResetTimer()
  1137  	for n := 0; n < b.N; n++ {
  1138  		for i := 0; i < 100; i++ {
  1139  			wgWrites.Add(1)
  1140  			c.QueueRPC(mockCall)
  1141  		}
  1142  		wgWrites.Wait()
  1143  	}
  1144  	// we don't care about cleaning up
  1145  }
  1146  
  1147  func BenchmarkSetReadDeadline(b *testing.B) {
  1148  	l, err := net.Listen("tcp", "localhost:0")
  1149  	if err != nil {
  1150  		b.Fatal(err)
  1151  	}
  1152  	var wg sync.WaitGroup
  1153  	wg.Add(1)
  1154  	go func() {
  1155  		conn, err := l.Accept()
  1156  		if err != nil {
  1157  			b.Error(err)
  1158  		}
  1159  		wg.Done()
  1160  		conn.Close()
  1161  	}()
  1162  
  1163  	conn, err := net.Dial("tcp", l.Addr().String())
  1164  	if err != nil {
  1165  		b.Fatal(err)
  1166  	}
  1167  
  1168  	b.ResetTimer()
  1169  	for i := 0; i < b.N; i++ {
  1170  		if err := conn.SetReadDeadline(time.Now().Add(DefaultReadTimeout)); err != nil {
  1171  			b.Fatal(err)
  1172  		}
  1173  	}
  1174  	b.StopTimer()
  1175  
  1176  	conn.Close()
  1177  	l.Close()
  1178  	wg.Wait()
  1179  }
  1180  
  1181  func TestBuffer(t *testing.T) {
  1182  	size := 42
  1183  	b := newBuffer(size)
  1184  	if cap(b) < size {
  1185  		t.Fatalf("Excpected cap >= %d, got %d", size, cap(b))
  1186  	}
  1187  	if len(b) != size {
  1188  		t.Fatalf("Excpected len %d, got %d", size, len(b))
  1189  	}
  1190  	freeBuffer(b)
  1191  
  1192  	size = 40
  1193  	b = newBuffer(size)
  1194  	if cap(b) < size {
  1195  		t.Fatalf("Excpected cap >= %d, got %d", size, cap(b))
  1196  	}
  1197  	if len(b) != size {
  1198  		t.Fatalf("Excpected len %d, got %d", size, len(b))
  1199  	}
  1200  	freeBuffer(b)
  1201  
  1202  	size = 45
  1203  	b = newBuffer(size)
  1204  	if cap(b) < size {
  1205  		t.Fatalf("Excpected cap >= %d, got %d", size, cap(b))
  1206  	}
  1207  	if len(b) != size {
  1208  		t.Fatalf("Excpected len %d, got %d", size, len(b))
  1209  	}
  1210  	freeBuffer(b)
  1211  }
  1212  
  1213  func TestMarshalJSON(t *testing.T) {
  1214  	ctrl := test.NewController(t)
  1215  	defer ctrl.Finish()
  1216  
  1217  	var localAddr net.Addr
  1218  	var remoteAddr net.Addr
  1219  	var id uint32 = 111
  1220  	tcp := "tcp"
  1221  	localIp := []byte("172.16.254.1")
  1222  	remoteIp := []byte("10.16.254.1")
  1223  	localAddr = &net.TCPAddr{IP: localIp, Port: 0, Zone: "testZone"}
  1224  	remoteAddr = &net.TCPAddr{IP: remoteIp, Port: 0, Zone: "testZone"}
  1225  
  1226  	mockConn := mock.NewMockConn(ctrl)
  1227  	mockConn.EXPECT().LocalAddr().Return(localAddr)
  1228  	mockConn.EXPECT().RemoteAddr().Return(remoteAddr)
  1229  	c := &client{
  1230  		conn:          mockConn,
  1231  		addr:          "testRegionServerAddress",
  1232  		ctype:         RegionClient,
  1233  		rpcs:          make(chan []hrpc.Call),
  1234  		done:          make(chan struct{}),
  1235  		sent:          make(map[uint32]hrpc.Call),
  1236  		rpcQueueSize:  1, // size one to skip sendBatch
  1237  		flushInterval: 1000 * time.Second,
  1238  		compressor:    &compressor{Codec: mockCodec{}},
  1239  		id:            id,
  1240  	}
  1241  
  1242  	jsonVal, err := c.MarshalJSON()
  1243  
  1244  	if err != nil {
  1245  		t.Fatalf("Did not expect Error to be thrown: %v", err)
  1246  	}
  1247  
  1248  	var jsonUnMarshal map[string]interface{}
  1249  	err = json.Unmarshal(jsonVal, &jsonUnMarshal)
  1250  
  1251  	if err != nil {
  1252  		t.Fatalf("Error while unmarshalling JSON, %v", err)
  1253  	}
  1254  
  1255  	actualLocalAddr := jsonUnMarshal["ConnectionLocalAddress"].(map[string]interface{})
  1256  	actualRemoteAddr := jsonUnMarshal["ConnectionRemoteAddress"].(map[string]interface{})
  1257  
  1258  	assert.Equal(t, tcp, actualLocalAddr["Network"])
  1259  	assert.Equal(t, tcp, actualRemoteAddr["Network"])
  1260  	assert.Equal(t, string(RegionClient), jsonUnMarshal["ClientType"])
  1261  	assert.Equal(t, float64(0), jsonUnMarshal["InFlight"])
  1262  	assert.Equal(t, float64(id), jsonUnMarshal["Id"])
  1263  
  1264  }
  1265  
  1266  func TestMarshalJSONNilValues(t *testing.T) {
  1267  	ctrl := test.NewController(t)
  1268  	defer ctrl.Finish()
  1269  
  1270  	c := &client{
  1271  		conn: nil,
  1272  		rpcs: nil,
  1273  		done: nil,
  1274  		sent: nil,
  1275  	}
  1276  
  1277  	_, err := c.MarshalJSON()
  1278  	if err != nil {
  1279  		t.Fatalf("Did not expect Error to be thrown: %v", err)
  1280  	}
  1281  }
  1282  
  1283  func BenchmarkSendScanRequest(b *testing.B) {
  1284  	var (
  1285  		table    = []byte("table")
  1286  		startRow = []byte("startRow")
  1287  		stopRow  = []byte("stopRow")
  1288  	)
  1289  	b.Run("NewScan", func(b *testing.B) {
  1290  		b.ReportAllocs()
  1291  		for i := 0; i < b.N; i++ {
  1292  			_, err := hrpc.NewScanRange(context.Background(), table, startRow, stopRow)
  1293  			if err != nil {
  1294  				b.Fatal(err)
  1295  			}
  1296  		}
  1297  	})
  1298  
  1299  	s, err := hrpc.NewScanRange(context.Background(), table, startRow, stopRow)
  1300  	if err != nil {
  1301  		b.Fatal(err)
  1302  	}
  1303  	s.SetRegion(
  1304  		NewInfo(0, nil, []byte("reg0"),
  1305  			[]byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil))
  1306  	b.Run("ToProto", func(b *testing.B) {
  1307  		b.ReportAllocs()
  1308  		for i := 0; i < b.N; i++ {
  1309  			s.ToProto()
  1310  		}
  1311  	})
  1312  
  1313  	request := s.ToProto()
  1314  	b.Run("marshalProto", func(b *testing.B) {
  1315  		b.ReportAllocs()
  1316  		for i := 0; i < b.N; i++ {
  1317  			marshalProto(s, 42, request, 0)
  1318  		}
  1319  	})
  1320  }