github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/mockrc_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  	"fmt"
    12  	"log/slog"
    13  	"net"
    14  	"sync"
    15  	"sync/atomic"
    16  	"time"
    17  
    18  	"github.com/tsuna/gohbase/compression"
    19  	"github.com/tsuna/gohbase/hrpc"
    20  	"github.com/tsuna/gohbase/pb"
    21  	"github.com/tsuna/gohbase/region"
    22  	"google.golang.org/protobuf/proto"
    23  )
    24  
    25  type testClient struct {
    26  	addr    string
    27  	numNSRE int32
    28  }
    29  
    30  var nsreRegion = &pb.Result{Cell: []*pb.Cell{
    31  	&pb.Cell{
    32  		Row:       []byte("nsre,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
    33  		Family:    []byte("info"),
    34  		Qualifier: []byte("regioninfo"),
    35  		Value: []byte("PBUF\b\xc4\xcd\xe9\x99\xe0)\x12\x0f\n\adefault\x12\x04nsre" +
    36  			"\x1a\x00\"\x00(\x000\x008\x00"),
    37  	},
    38  	&pb.Cell{
    39  		Row:       []byte("nsre,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
    40  		Family:    []byte("info"),
    41  		Qualifier: []byte("seqnumDuringOpen"),
    42  		Value:     []byte("\x00\x00\x00\x00\x00\x00\x00\x02"),
    43  	},
    44  	&pb.Cell{
    45  		Row:       []byte("nsre,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
    46  		Family:    []byte("info"),
    47  		Qualifier: []byte("server"),
    48  		Value:     []byte("regionserver:1"),
    49  	},
    50  	&pb.Cell{
    51  		Row:       []byte("nsre,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
    52  		Family:    []byte("info"),
    53  		Qualifier: []byte("serverstartcode"),
    54  		Value:     []byte("\x00\x00\x01N\x02\x92R\xb1"),
    55  	},
    56  }}
    57  
    58  // makeRegionResult returns a region that spans the whole table
    59  // and uses name of the table as the hostname of the regionserver
    60  func makeRegionResult(key []byte) *pb.ScanResponse {
    61  	s := bytes.SplitN(key, []byte(","), 2)
    62  	fqtable := s[0]
    63  
    64  	row := append(fqtable, []byte(",,1434573235908.56f833d5569a27c7a43fbf547b4924a4.")...)
    65  	t := bytes.SplitN(fqtable, []byte{':'}, 2)
    66  	var namespace, table []byte
    67  	if len(t) == 2 {
    68  		namespace = t[0]
    69  		table = t[1]
    70  	} else {
    71  		namespace = []byte("default")
    72  		table = fqtable
    73  	}
    74  	regionInfo := &pb.RegionInfo{
    75  		RegionId: proto.Uint64(1434573235908),
    76  		TableName: &pb.TableName{
    77  			Namespace: namespace,
    78  			Qualifier: table,
    79  		},
    80  		Offline: proto.Bool(false),
    81  	}
    82  	regionInfoValue, err := proto.Marshal(regionInfo)
    83  	if err != nil {
    84  		panic(err)
    85  	}
    86  	regionInfoValue = append([]byte("PBUF"), regionInfoValue...)
    87  
    88  	return &pb.ScanResponse{Results: []*pb.Result{
    89  		&pb.Result{Cell: []*pb.Cell{
    90  			&pb.Cell{
    91  				Row:       row,
    92  				Family:    []byte("info"),
    93  				Qualifier: []byte("regioninfo"),
    94  				Value:     regionInfoValue,
    95  			},
    96  			&pb.Cell{
    97  				Row:       row,
    98  				Family:    []byte("info"),
    99  				Qualifier: []byte("seqnumDuringOpen"),
   100  				Value:     []byte("\x00\x00\x00\x00\x00\x00\x00\x02"),
   101  			},
   102  			&pb.Cell{
   103  				Row:       row,
   104  				Family:    []byte("info"),
   105  				Qualifier: []byte("server"),
   106  				Value:     fqtable,
   107  			},
   108  			&pb.Cell{
   109  				Row:       row,
   110  				Family:    []byte("info"),
   111  				Qualifier: []byte("serverstartcode"),
   112  				Value:     []byte("\x00\x00\x01N\x02\x92R\xb1"),
   113  			},
   114  		}}}}
   115  }
   116  
   117  var metaRow = &pb.Result{Cell: []*pb.Cell{
   118  	&pb.Cell{
   119  		Row:       []byte("test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
   120  		Family:    []byte("info"),
   121  		Qualifier: []byte("regioninfo"),
   122  		Value: []byte("PBUF\b\xc4\xcd\xe9\x99\xe0)\x12\x0f\n\adefault\x12\x04test" +
   123  			"\x1a\x00\"\x00(\x000\x008\x00"),
   124  	},
   125  	&pb.Cell{
   126  		Row:       []byte("test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
   127  		Family:    []byte("info"),
   128  		Qualifier: []byte("seqnumDuringOpen"),
   129  		Value:     []byte("\x00\x00\x00\x00\x00\x00\x00\x02"),
   130  	},
   131  	&pb.Cell{
   132  		Row:       []byte("test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
   133  		Family:    []byte("info"),
   134  		Qualifier: []byte("server"),
   135  		Value:     []byte("regionserver:2"),
   136  	},
   137  	&pb.Cell{
   138  		Row:       []byte("test,,1434573235908.56f833d5569a27c7a43fbf547b4924a4."),
   139  		Family:    []byte("info"),
   140  		Qualifier: []byte("serverstartcode"),
   141  		Value:     []byte("\x00\x00\x01N\x02\x92R\xb1"),
   142  	},
   143  }}
   144  
   145  var test1SplitA = &pb.Result{Cell: []*pb.Cell{
   146  	&pb.Cell{
   147  		Row:       []byte("test1,,1480547738107.825c5c7e480c76b73d6d2bad5d3f7bb8."),
   148  		Family:    []byte("info"),
   149  		Qualifier: []byte("regioninfo"),
   150  		Value: []byte("PBUF\b\xfbÖ\xbc\x8b+\x12\x10\n\adefault\x12\x05" +
   151  			"test1\x1a\x00\"\x03baz(\x000\x008\x00"),
   152  	},
   153  	&pb.Cell{
   154  		Row:       []byte("test1,,1480547738107.825c5c7e480c76b73d6d2bad5d3f7bb8."),
   155  		Family:    []byte("info"),
   156  		Qualifier: []byte("seqnumDuringOpen"),
   157  		Value:     []byte("\x00\x00\x00\x00\x00\x00\x00\v"),
   158  	},
   159  	&pb.Cell{
   160  		Row:       []byte("test1,,1480547738107.825c5c7e480c76b73d6d2bad5d3f7bb8."),
   161  		Family:    []byte("info"),
   162  		Qualifier: []byte("server"),
   163  		Value:     []byte("regionserver:1"),
   164  	},
   165  	&pb.Cell{
   166  		Row:       []byte("test1,,1480547738107.825c5c7e480c76b73d6d2bad5d3f7bb8."),
   167  		Family:    []byte("info"),
   168  		Qualifier: []byte("serverstartcode"),
   169  		Value:     []byte("\x00\x00\x01X\xb6\x83^3"),
   170  	},
   171  }}
   172  
   173  var m sync.RWMutex
   174  var clients map[string]uint32
   175  
   176  func init() {
   177  	clients = make(map[string]uint32)
   178  }
   179  
   180  func newMockRegionClient(addr string, ctype region.ClientType, queueSize int,
   181  	flushInterval time.Duration, effectiveUser string,
   182  	readTimeout time.Duration, codec compression.Codec,
   183  	dialer func(ctx context.Context, network, addr string) (net.Conn, error),
   184  	log *slog.Logger) hrpc.RegionClient {
   185  	m.Lock()
   186  	clients[addr]++
   187  	m.Unlock()
   188  	return &testClient{addr: addr}
   189  }
   190  
   191  func (c *testClient) Dial(ctx context.Context) error {
   192  	return nil
   193  }
   194  
   195  func (c *testClient) Addr() string {
   196  	return c.addr
   197  }
   198  
   199  func (c *testClient) String() string {
   200  	return fmt.Sprintf("RegionClient{Addr: %s}", c.addr)
   201  }
   202  
   203  func (c *testClient) QueueRPC(call hrpc.Call) {
   204  	// ignore timed out rpcs to mock the region client
   205  	select {
   206  	case <-call.Context().Done():
   207  		return
   208  	default:
   209  	}
   210  	if !bytes.Equal(call.Table(), []byte("hbase:meta")) {
   211  		_, ok := call.(*hrpc.Get)
   212  		if !ok || !bytes.HasSuffix(call.Key(), bytes.Repeat([]byte{0}, 17)) {
   213  			// not a get and not a region probe
   214  			// just return as the mock call should just populate the ResultChan in test
   215  			return
   216  		}
   217  		// region probe, fail for the nsre region 3 times to force retry
   218  		if bytes.Equal(call.Table(), []byte("nsre")) {
   219  			i := atomic.AddInt32(&c.numNSRE, 1)
   220  			if i <= 3 {
   221  				call.ResultChan() <- hrpc.RPCResult{Error: region.NotServingRegionError{}}
   222  				return
   223  			}
   224  		}
   225  		m.RLock()
   226  		i := clients[c.addr]
   227  		m.RUnlock()
   228  
   229  		// if we are connected to this client the first time,
   230  		// pretend it's down to fail the probe and start a reconnect
   231  		if bytes.Equal(call.Table(), []byte("down")) {
   232  			if i <= 1 {
   233  				call.ResultChan() <- hrpc.RPCResult{Error: region.ServerError{}}
   234  			} else {
   235  				// otherwise, the region is fine
   236  				call.ResultChan() <- hrpc.RPCResult{}
   237  			}
   238  			return
   239  		}
   240  	}
   241  	if bytes.HasSuffix(call.Key(), bytes.Repeat([]byte{0}, 17)) {
   242  		// meta region probe, return empty to signify that region is online
   243  		call.ResultChan() <- hrpc.RPCResult{}
   244  	} else if bytes.HasPrefix(call.Key(), []byte("test,")) {
   245  		call.ResultChan() <- hrpc.RPCResult{Msg: &pb.ScanResponse{
   246  			Results: []*pb.Result{metaRow}}}
   247  	} else if bytes.HasPrefix(call.Key(), []byte("test1,,")) {
   248  		call.ResultChan() <- hrpc.RPCResult{Msg: &pb.ScanResponse{
   249  			Results: []*pb.Result{test1SplitA}}}
   250  	} else if bytes.HasPrefix(call.Key(), []byte("nsre,,")) {
   251  		call.ResultChan() <- hrpc.RPCResult{Msg: &pb.ScanResponse{
   252  			Results: []*pb.Result{nsreRegion}}}
   253  	} else if bytes.HasPrefix(call.Key(), []byte("tablenotfound,")) {
   254  		call.ResultChan() <- hrpc.RPCResult{Msg: &pb.ScanResponse{
   255  			Results:     []*pb.Result{},
   256  			MoreResults: proto.Bool(false),
   257  		}}
   258  	} else {
   259  		call.ResultChan() <- hrpc.RPCResult{Msg: makeRegionResult(call.Key())}
   260  	}
   261  }
   262  
   263  func (c *testClient) QueueBatch(ctx context.Context, batch []hrpc.Call) {
   264  	// do nothing. Let the test fill in result.
   265  }
   266  
   267  func (c *testClient) Close() {}