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

     1  // Copyright (C) 2017  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  	"errors"
    12  	"fmt"
    13  	"sort"
    14  	"strconv"
    15  	"testing"
    16  
    17  	"github.com/tsuna/gohbase/hrpc"
    18  	"github.com/tsuna/gohbase/pb"
    19  	"github.com/tsuna/gohbase/test"
    20  	"google.golang.org/protobuf/proto"
    21  )
    22  
    23  type bytesSlice [][]byte
    24  
    25  func (p bytesSlice) Len() int           { return len(p) }
    26  func (p bytesSlice) Less(i, j int) bool { return bytes.Compare(p[i], p[j]) < 0 }
    27  func (p bytesSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    28  
    29  type RegionActions []*pb.RegionAction
    30  
    31  func (a RegionActions) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
    32  func (a RegionActions) Len() int      { return len(a) }
    33  func (a RegionActions) Less(i, j int) bool {
    34  	return bytes.Compare(a[i].Region.Value, a[j].Region.Value) < 0
    35  }
    36  
    37  var (
    38  	reg0 = NewInfo(0, nil, []byte("reg0"),
    39  		[]byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil)
    40  	reg1 = NewInfo(0, nil, []byte("reg1"),
    41  		[]byte("reg1,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil)
    42  	reg2 = NewInfo(0, nil, []byte("reg2"),
    43  		[]byte("reg2,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil)
    44  )
    45  
    46  func bytesSlicesEqual(a, b [][]byte) bool {
    47  	if len(a) != len(b) {
    48  		return false
    49  	}
    50  	for i := range a {
    51  		if !bytes.Equal(a[i], b[i]) {
    52  			return false
    53  		}
    54  	}
    55  	return true
    56  }
    57  
    58  func bytesSlicesLen(a [][]byte) uint32 {
    59  	var l uint32
    60  	for _, b := range a {
    61  		l += uint32(len(b))
    62  	}
    63  	return l
    64  }
    65  
    66  func TestMultiToProto(t *testing.T) {
    67  	ctrl := test.NewController(t)
    68  	defer ctrl.Finish()
    69  
    70  	values := map[string]map[string][]byte{
    71  		"cf": map[string][]byte{
    72  			"c": []byte("v"),
    73  		},
    74  	}
    75  	valuesProto := []*pb.MutationProto_ColumnValue{
    76  		&pb.MutationProto_ColumnValue{
    77  			Family: []byte("cf"),
    78  			QualifierValue: []*pb.MutationProto_ColumnValue_QualifierValue{
    79  				&pb.MutationProto_ColumnValue_QualifierValue{
    80  					Qualifier: []byte("c"),
    81  					Value:     []byte("v"),
    82  				},
    83  			},
    84  		},
    85  	}
    86  	delValues := map[string]map[string][]byte{
    87  		"cf": map[string][]byte{
    88  			"c": nil,
    89  		},
    90  	}
    91  	delProto := []*pb.MutationProto_ColumnValue{
    92  		&pb.MutationProto_ColumnValue{
    93  			Family: []byte("cf"),
    94  			QualifierValue: []*pb.MutationProto_ColumnValue_QualifierValue{
    95  				&pb.MutationProto_ColumnValue_QualifierValue{
    96  					Qualifier:  []byte("c"),
    97  					DeleteType: pb.MutationProto_DELETE_MULTIPLE_VERSIONS.Enum(),
    98  				},
    99  			},
   100  		},
   101  	}
   102  	appendProto := []*pb.MutationProto_ColumnValue{
   103  		&pb.MutationProto_ColumnValue{
   104  			Family: []byte("cf"),
   105  			QualifierValue: []*pb.MutationProto_ColumnValue_QualifierValue{
   106  				&pb.MutationProto_ColumnValue_QualifierValue{
   107  					Qualifier: []byte("c"),
   108  				},
   109  			},
   110  		},
   111  	}
   112  
   113  	tests := []struct {
   114  		calls           []hrpc.Call
   115  		out             *pb.MultiRequest
   116  		panicMsg        string
   117  		cellblocksProto *pb.MultiRequest
   118  		cellblocks      [][]byte
   119  		cellblocksLen   uint32
   120  	}{
   121  		{
   122  			calls: func() []hrpc.Call {
   123  				cs := make([]hrpc.Call, 5)
   124  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   125  				cs[0].SetRegion(reg0)
   126  				cs[1], _ = hrpc.NewPutStr(context.Background(), "reg0", "call1", values)
   127  				cs[1].SetRegion(reg0)
   128  				cs[2], _ = hrpc.NewAppStr(context.Background(), "reg1", "call2", values)
   129  				cs[2].SetRegion(reg1)
   130  				cs[3], _ = hrpc.NewDelStr(context.Background(), "reg1", "call3", delValues)
   131  				cs[3].SetRegion(reg1)
   132  				cs[4], _ = hrpc.NewIncStr(context.Background(), "reg2", "call4", delValues)
   133  				cs[4].SetRegion(reg2)
   134  				return cs
   135  			}(),
   136  			out: &pb.MultiRequest{
   137  				RegionAction: []*pb.RegionAction{
   138  					&pb.RegionAction{
   139  						Region: &pb.RegionSpecifier{
   140  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   141  							Value: []byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   142  						},
   143  						Action: []*pb.Action{
   144  							&pb.Action{Index: proto.Uint32(1), Get: &pb.Get{
   145  								Row: []byte("call0"), TimeRange: &pb.TimeRange{}}},
   146  							&pb.Action{Index: proto.Uint32(2), Mutation: &pb.MutationProto{
   147  								Row:         []byte("call1"),
   148  								MutateType:  pb.MutationProto_PUT.Enum(),
   149  								Durability:  pb.MutationProto_USE_DEFAULT.Enum(),
   150  								ColumnValue: valuesProto,
   151  							}},
   152  						},
   153  					},
   154  					&pb.RegionAction{
   155  						Region: &pb.RegionSpecifier{
   156  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   157  							Value: []byte("reg1,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   158  						},
   159  						Action: []*pb.Action{
   160  							&pb.Action{Index: proto.Uint32(3), Mutation: &pb.MutationProto{
   161  								Row:         []byte("call2"),
   162  								MutateType:  pb.MutationProto_APPEND.Enum(),
   163  								Durability:  pb.MutationProto_USE_DEFAULT.Enum(),
   164  								ColumnValue: valuesProto,
   165  							}},
   166  							&pb.Action{Index: proto.Uint32(4), Mutation: &pb.MutationProto{
   167  								Row:         []byte("call3"),
   168  								MutateType:  pb.MutationProto_DELETE.Enum(),
   169  								Durability:  pb.MutationProto_USE_DEFAULT.Enum(),
   170  								ColumnValue: delProto,
   171  							}},
   172  						},
   173  					},
   174  					&pb.RegionAction{
   175  						Region: &pb.RegionSpecifier{
   176  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   177  							Value: []byte("reg2,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   178  						},
   179  						Action: []*pb.Action{
   180  							&pb.Action{Index: proto.Uint32(5), Mutation: &pb.MutationProto{
   181  								Row:         []byte("call4"),
   182  								MutateType:  pb.MutationProto_INCREMENT.Enum(),
   183  								Durability:  pb.MutationProto_USE_DEFAULT.Enum(),
   184  								ColumnValue: appendProto,
   185  							}},
   186  						},
   187  					},
   188  				},
   189  			},
   190  			cellblocksProto: &pb.MultiRequest{
   191  				RegionAction: []*pb.RegionAction{
   192  					&pb.RegionAction{
   193  						Region: &pb.RegionSpecifier{
   194  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   195  							Value: []byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   196  						},
   197  						Action: []*pb.Action{
   198  							&pb.Action{Index: proto.Uint32(1), Get: &pb.Get{
   199  								Row: []byte("call0"), TimeRange: &pb.TimeRange{}}},
   200  							&pb.Action{Index: proto.Uint32(2), Mutation: &pb.MutationProto{
   201  								Row:                 []byte("call1"),
   202  								MutateType:          pb.MutationProto_PUT.Enum(),
   203  								Durability:          pb.MutationProto_USE_DEFAULT.Enum(),
   204  								AssociatedCellCount: proto.Int32(1),
   205  							}},
   206  						},
   207  					},
   208  					&pb.RegionAction{
   209  						Region: &pb.RegionSpecifier{
   210  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   211  							Value: []byte("reg1,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   212  						},
   213  						Action: []*pb.Action{
   214  							&pb.Action{Index: proto.Uint32(3), Mutation: &pb.MutationProto{
   215  								Row:                 []byte("call2"),
   216  								MutateType:          pb.MutationProto_APPEND.Enum(),
   217  								Durability:          pb.MutationProto_USE_DEFAULT.Enum(),
   218  								AssociatedCellCount: proto.Int32(1),
   219  							}},
   220  							&pb.Action{Index: proto.Uint32(4), Mutation: &pb.MutationProto{
   221  								Row:                 []byte("call3"),
   222  								MutateType:          pb.MutationProto_DELETE.Enum(),
   223  								Durability:          pb.MutationProto_USE_DEFAULT.Enum(),
   224  								AssociatedCellCount: proto.Int32(1),
   225  							}},
   226  						},
   227  					},
   228  					&pb.RegionAction{
   229  						Region: &pb.RegionSpecifier{
   230  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   231  							Value: []byte("reg2,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   232  						},
   233  						Action: []*pb.Action{
   234  							&pb.Action{Index: proto.Uint32(5), Mutation: &pb.MutationProto{
   235  								Row:                 []byte("call4"),
   236  								MutateType:          pb.MutationProto_INCREMENT.Enum(),
   237  								Durability:          pb.MutationProto_USE_DEFAULT.Enum(),
   238  								AssociatedCellCount: proto.Int32(1),
   239  							}},
   240  						},
   241  					},
   242  				},
   243  			},
   244  			cellblocksLen: 130,
   245  			cellblocks: [][]byte{
   246  				[]byte("\x00\x00\x00\x1d\x00\x00\x00\x14\x00\x00\x00\x01\x00\x05" +
   247  					"call1" + "\x02" + "cf" + "c" +
   248  					"\u007f\xff\xff\xff\xff\xff\xff\xff\x04" + "v"),
   249  				[]byte("\x00\x00\x00\x1d\x00\x00\x00\x14\x00\x00\x00\x01\x00\x05" +
   250  					"call2" + "\x02" + "cf" + "c" +
   251  					"\u007f\xff\xff\xff\xff\xff\xff\xff\x04" + "v"),
   252  				[]byte("\x00\x00\x00\x1c\x00\x00\x00\x14\x00\x00\x00\x00\x00\x05" +
   253  					"call3" + "\x02" + "cf" + "c" +
   254  					"\u007f\xff\xff\xff\xff\xff\xff\xff\f"),
   255  				[]byte("\x00\x00\x00\x1c\x00\x00\x00\x14\x00\x00\x00\x00\x00\x05" +
   256  					"call4" + "\x02" + "cf" + "c" +
   257  					"\u007f\xff\xff\xff\xff\xff\xff\xff\x04")},
   258  		},
   259  		{ // one call with expired context
   260  			calls: func() []hrpc.Call {
   261  				cs := make([]hrpc.Call, 2)
   262  				ctx, cancel := context.WithCancel(context.Background())
   263  				cancel()
   264  				cs[0], _ = hrpc.NewGetStr(ctx, "reg0", "call0")
   265  				cs[0].SetRegion(reg0)
   266  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg0", "call1", nil)
   267  				cs[1].SetRegion(reg0)
   268  				return cs
   269  			}(),
   270  			out: &pb.MultiRequest{
   271  				RegionAction: []*pb.RegionAction{
   272  					&pb.RegionAction{
   273  						Region: &pb.RegionSpecifier{
   274  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   275  							Value: []byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   276  						},
   277  						Action: []*pb.Action{
   278  							&pb.Action{Index: proto.Uint32(2), Mutation: &pb.MutationProto{
   279  								Row:        []byte("call1"),
   280  								MutateType: pb.MutationProto_APPEND.Enum(),
   281  								Durability: pb.MutationProto_USE_DEFAULT.Enum(),
   282  							}},
   283  						},
   284  					},
   285  				},
   286  			},
   287  			cellblocksProto: &pb.MultiRequest{
   288  				RegionAction: []*pb.RegionAction{
   289  					&pb.RegionAction{
   290  						Region: &pb.RegionSpecifier{
   291  							Type:  pb.RegionSpecifier_REGION_NAME.Enum(),
   292  							Value: []byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."),
   293  						},
   294  						Action: []*pb.Action{
   295  							&pb.Action{Index: proto.Uint32(2), Mutation: &pb.MutationProto{
   296  								Row:                 []byte("call1"),
   297  								MutateType:          pb.MutationProto_APPEND.Enum(),
   298  								Durability:          pb.MutationProto_USE_DEFAULT.Enum(),
   299  								AssociatedCellCount: proto.Int32(0),
   300  							}},
   301  						},
   302  					},
   303  				},
   304  			},
   305  		},
   306  		{ // one batched call is not supported for batching
   307  			calls: func() []hrpc.Call {
   308  				cs := make([]hrpc.Call, 2)
   309  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "yolo")
   310  				cs[0].SetRegion(reg0)
   311  
   312  				cs[1] = hrpc.NewCreateTable(context.Background(), []byte("yolo"), nil)
   313  				return cs
   314  			}(),
   315  			panicMsg: "unsupported call type for Multi: *hrpc.CreateTable",
   316  		},
   317  	}
   318  
   319  	for i, tcase := range tests {
   320  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   321  			m := newMulti(1000)
   322  
   323  			if m.add(tcase.calls) {
   324  				t.Fatal("multi is full")
   325  			}
   326  
   327  			if tcase.panicMsg != "" {
   328  				defer func() {
   329  					r := recover()
   330  					msg, ok := r.(string)
   331  					if r == nil || !ok || msg != tcase.panicMsg {
   332  						t.Errorf("expected panic with %q, got %v", tcase.panicMsg, r)
   333  					}
   334  				}()
   335  				_ = m.ToProto()
   336  				return
   337  			}
   338  
   339  			// test proto
   340  			p := m.ToProto()
   341  
   342  			out, ok := p.(*pb.MultiRequest)
   343  			if !ok {
   344  				t.Fatalf("unexpected proto type %T", p)
   345  			}
   346  
   347  			// check that we recorded correct ordering RegionActions
   348  			if exp, got := len(m.regions), len(out.RegionAction); exp != got {
   349  				t.Fatalf("expected regions length %d, got %d", exp, got)
   350  			}
   351  			for i, r := range m.regions {
   352  				if exp, got := r.Name(), out.RegionAction[i].Region.Value; !bytes.Equal(exp, got) {
   353  					t.Fatalf("regions are different at index %d: %q != %q", i, exp, got)
   354  				}
   355  			}
   356  
   357  			// compare that the MultiRequest are as expected
   358  			sort.Sort(RegionActions(tcase.out.RegionAction))
   359  			sort.Sort(RegionActions(out.RegionAction))
   360  
   361  			if !proto.Equal(tcase.out, out) {
   362  				t.Fatalf("expected %v, got %v", tcase.out, out)
   363  			}
   364  
   365  			// test cellblocks
   366  			m = newMulti(1000)
   367  			if m.add(tcase.calls) {
   368  				t.Fatal("multi is full")
   369  			}
   370  			cellblocksProto, cellblocks, cellblocksLen := m.SerializeCellBlocks(nil)
   371  			out, ok = cellblocksProto.(*pb.MultiRequest)
   372  			if !ok {
   373  				t.Fatalf("unexpected proto type %T", cellblocksProto)
   374  			}
   375  
   376  			sort.Sort(RegionActions(tcase.cellblocksProto.RegionAction))
   377  			sort.Sort(RegionActions(out.RegionAction))
   378  
   379  			if !proto.Equal(tcase.cellblocksProto, out) {
   380  				t.Errorf("expected cellblocks proto %v, got %v",
   381  					tcase.cellblocksProto, cellblocksProto)
   382  			}
   383  
   384  			if cellblocksLen != tcase.cellblocksLen {
   385  				t.Errorf("expected cellblocks length %d, got %d",
   386  					tcase.cellblocksLen, cellblocksLen)
   387  			}
   388  
   389  			// check total length matches the returned
   390  			if l := bytesSlicesLen(cellblocks); l != cellblocksLen {
   391  				t.Errorf("total length of cellblocks %d doesn't match returned %d",
   392  					l, cellblocksLen)
   393  			}
   394  
   395  			// because maps are iterated in random order, the best we can do here
   396  			// is sort cellblocks and make sure that each byte slice equals. This doesn't
   397  			// test that byte slices are written out in the correct order.
   398  			expected := make([][]byte, len(tcase.cellblocks))
   399  			copy(expected, tcase.cellblocks)
   400  			got := make([][]byte, len(cellblocks))
   401  			copy(got, cellblocks)
   402  
   403  			sort.Sort(bytesSlice(expected))
   404  			sort.Sort(bytesSlice(got))
   405  			if !bytesSlicesEqual(expected, got) {
   406  				t.Errorf("expected cellblocks %q, got %q", tcase.cellblocks, cellblocks)
   407  			}
   408  		})
   409  	}
   410  }
   411  
   412  func TestMultiReturnResults(t *testing.T) {
   413  	tests := []struct {
   414  		calls       []hrpc.Call
   415  		regions     []hrpc.RegionInfo
   416  		response    proto.Message
   417  		err         error
   418  		shouldPanic bool
   419  		out         []hrpc.RPCResult
   420  	}{
   421  		{ // all good
   422  			calls: func() []hrpc.Call {
   423  				cs := make([]hrpc.Call, 5)
   424  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   425  				cs[0].SetRegion(reg0)
   426  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg1", "call1", nil)
   427  				cs[1].SetRegion(reg1)
   428  				cs[2], _ = hrpc.NewAppStr(context.Background(), "reg0", "call2", nil)
   429  				cs[2].SetRegion(reg0)
   430  				cs[3], _ = hrpc.NewGetStr(context.Background(), "reg2", "call3")
   431  				cs[3].SetRegion(reg2)
   432  				cs[4], _ = hrpc.NewAppStr(context.Background(), "reg1", "call4", nil)
   433  				cs[4].SetRegion(reg1)
   434  				return cs
   435  			}(),
   436  			regions: []hrpc.RegionInfo{reg2, reg0, reg1},
   437  			response: &pb.MultiResponse{
   438  				RegionActionResult: []*pb.RegionActionResult{
   439  					// reg2
   440  					&pb.RegionActionResult{
   441  						ResultOrException: []*pb.ResultOrException{
   442  							&pb.ResultOrException{
   443  								Index: proto.Uint32(4),
   444  								Result: &pb.Result{
   445  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call3")}},
   446  								},
   447  							},
   448  						},
   449  					},
   450  					// reg0
   451  					&pb.RegionActionResult{
   452  						ResultOrException: []*pb.ResultOrException{
   453  							&pb.ResultOrException{
   454  								Index: proto.Uint32(1),
   455  								Result: &pb.Result{
   456  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   457  								},
   458  							},
   459  							&pb.ResultOrException{
   460  								Index: proto.Uint32(3),
   461  								Result: &pb.Result{
   462  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call2")}},
   463  								},
   464  							},
   465  						},
   466  					},
   467  					// reg1, results are returned in different order
   468  					&pb.RegionActionResult{
   469  						ResultOrException: []*pb.ResultOrException{
   470  							&pb.ResultOrException{
   471  								Index: proto.Uint32(5),
   472  								Result: &pb.Result{
   473  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call4")}},
   474  								},
   475  							},
   476  							&pb.ResultOrException{
   477  								Index: proto.Uint32(2),
   478  								Result: &pb.Result{
   479  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call1")}},
   480  								},
   481  							},
   482  						},
   483  					},
   484  				},
   485  			},
   486  			out: []hrpc.RPCResult{
   487  				hrpc.RPCResult{Msg: &pb.GetResponse{Result: &pb.Result{
   488  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   489  				}}},
   490  				hrpc.RPCResult{Msg: &pb.MutateResponse{Result: &pb.Result{
   491  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call1")}},
   492  				}}},
   493  				hrpc.RPCResult{Msg: &pb.MutateResponse{Result: &pb.Result{
   494  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call2")}},
   495  				}}},
   496  				hrpc.RPCResult{Msg: &pb.GetResponse{Result: &pb.Result{
   497  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call3")}},
   498  				}}},
   499  				hrpc.RPCResult{Msg: &pb.MutateResponse{Result: &pb.Result{
   500  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call4")}},
   501  				}}},
   502  			},
   503  		},
   504  		{ // a region exception
   505  			calls: func() []hrpc.Call {
   506  				cs := make([]hrpc.Call, 4)
   507  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   508  				cs[0].SetRegion(reg0)
   509  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg1", "call1", nil)
   510  				cs[1].SetRegion(reg1)
   511  				cs[2], _ = hrpc.NewAppStr(context.Background(), "reg0", "call2", nil)
   512  				cs[2].SetRegion(reg0)
   513  				cs[3], _ = hrpc.NewAppStr(context.Background(), "reg1", "call3", nil)
   514  				cs[3].SetRegion(reg1)
   515  				return cs
   516  			}(),
   517  			regions: []hrpc.RegionInfo{reg1, reg0},
   518  			response: &pb.MultiResponse{
   519  				RegionActionResult: []*pb.RegionActionResult{
   520  					// reg1
   521  					&pb.RegionActionResult{
   522  						Exception: &pb.NameBytesPair{ // retryable exception
   523  							Name: proto.String(
   524  								"org.apache.hadoop.hbase.NotServingRegionException"),
   525  							Value: []byte("YOLO"),
   526  						},
   527  					},
   528  					// reg0, results different order
   529  					&pb.RegionActionResult{
   530  						ResultOrException: []*pb.ResultOrException{
   531  							&pb.ResultOrException{
   532  								Index: proto.Uint32(3),
   533  								Result: &pb.Result{
   534  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call2")}},
   535  								},
   536  							},
   537  							&pb.ResultOrException{
   538  								Index: proto.Uint32(1),
   539  								Result: &pb.Result{
   540  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   541  								},
   542  							},
   543  						},
   544  					},
   545  				},
   546  			},
   547  			out: []hrpc.RPCResult{
   548  				hrpc.RPCResult{Msg: &pb.GetResponse{Result: &pb.Result{
   549  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   550  				}}},
   551  				hrpc.RPCResult{Error: NotServingRegionError{errors.New("HBase Java " +
   552  					"exception org.apache.hadoop.hbase.NotServingRegionException:\nYOLO")}},
   553  				hrpc.RPCResult{Msg: &pb.MutateResponse{Result: &pb.Result{
   554  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call2")}},
   555  				}}},
   556  				hrpc.RPCResult{Error: NotServingRegionError{errors.New("HBase Java " +
   557  					"exception org.apache.hadoop.hbase.NotServingRegionException:\nYOLO")}},
   558  			},
   559  		},
   560  		{ // a result exception
   561  			calls: func() []hrpc.Call {
   562  				cs := make([]hrpc.Call, 2)
   563  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   564  				cs[0].SetRegion(reg0)
   565  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg0", "call1", nil)
   566  				cs[1].SetRegion(reg0)
   567  				return cs
   568  			}(),
   569  			regions: []hrpc.RegionInfo{reg0},
   570  			response: &pb.MultiResponse{
   571  				RegionActionResult: []*pb.RegionActionResult{
   572  					&pb.RegionActionResult{
   573  						ResultOrException: []*pb.ResultOrException{
   574  							&pb.ResultOrException{
   575  								Index: proto.Uint32(1),
   576  								Exception: &pb.NameBytesPair{
   577  									Name:  proto.String("YOLO"),
   578  									Value: []byte("SWAG"),
   579  								},
   580  							},
   581  							&pb.ResultOrException{
   582  								Index: proto.Uint32(2),
   583  								Result: &pb.Result{
   584  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call1")}},
   585  								},
   586  							},
   587  						},
   588  					},
   589  				},
   590  			},
   591  			out: []hrpc.RPCResult{
   592  				hrpc.RPCResult{Error: errors.New("HBase Java exception YOLO:\nSWAG")},
   593  				hrpc.RPCResult{Msg: &pb.MutateResponse{Result: &pb.Result{
   594  					Cell: []*pb.Cell{&pb.Cell{Row: []byte("call1")}},
   595  				}}},
   596  			},
   597  		},
   598  		{ // an unrecoverable error
   599  			calls: func() []hrpc.Call {
   600  				cs := make([]hrpc.Call, 2)
   601  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   602  				cs[0].SetRegion(reg0)
   603  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg1", "call1", nil)
   604  				cs[1].SetRegion(reg1)
   605  				return cs
   606  			}(),
   607  			err: ServerError{errors.New("OOOPS")},
   608  			out: []hrpc.RPCResult{
   609  				hrpc.RPCResult{Error: ServerError{errors.New("OOOPS")}},
   610  				hrpc.RPCResult{Error: ServerError{errors.New("OOOPS")}},
   611  			},
   612  		},
   613  		{ // non-MultiResponse
   614  			shouldPanic: true,
   615  			response:    &pb.CreateTableResponse{},
   616  		},
   617  		{ // non-Get or non-Mutate request
   618  			shouldPanic: true,
   619  			calls: []hrpc.Call{
   620  				hrpc.NewCreateTable(context.Background(), []byte("reg0"), nil),
   621  			},
   622  			response: &pb.MultiResponse{
   623  				RegionActionResult: []*pb.RegionActionResult{
   624  					&pb.RegionActionResult{
   625  						ResultOrException: []*pb.ResultOrException{
   626  							&pb.ResultOrException{
   627  								Index: proto.Uint32(1),
   628  								Result: &pb.Result{
   629  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   630  								},
   631  							},
   632  						},
   633  					},
   634  				},
   635  			},
   636  		},
   637  		{ // result index is 0
   638  			shouldPanic: true,
   639  			calls: func() []hrpc.Call {
   640  				cs := make([]hrpc.Call, 1)
   641  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   642  				cs[0].SetRegion(reg0)
   643  				return cs
   644  			}(),
   645  			regions: []hrpc.RegionInfo{reg0},
   646  			response: &pb.MultiResponse{
   647  				RegionActionResult: []*pb.RegionActionResult{
   648  					&pb.RegionActionResult{
   649  						ResultOrException: []*pb.ResultOrException{
   650  							&pb.ResultOrException{
   651  								Index: proto.Uint32(0),
   652  								Result: &pb.Result{
   653  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   654  								},
   655  							},
   656  						},
   657  					},
   658  				},
   659  			},
   660  		},
   661  		{ // result index is out of bounds
   662  			shouldPanic: true,
   663  			calls: func() []hrpc.Call {
   664  				cs := make([]hrpc.Call, 1)
   665  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   666  				cs[0].SetRegion(reg0)
   667  				return cs
   668  			}(),
   669  			regions: []hrpc.RegionInfo{reg0},
   670  			response: &pb.MultiResponse{
   671  				RegionActionResult: []*pb.RegionActionResult{
   672  					&pb.RegionActionResult{
   673  						ResultOrException: []*pb.ResultOrException{
   674  							&pb.ResultOrException{
   675  								Index: proto.Uint32(2),
   676  								Result: &pb.Result{
   677  									Cell: []*pb.Cell{&pb.Cell{Row: []byte("call0")}},
   678  								},
   679  							},
   680  						},
   681  					},
   682  				},
   683  			},
   684  		},
   685  	}
   686  
   687  	for i, tcase := range tests {
   688  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   689  			m := newMulti(1000)
   690  
   691  			if m.add(tcase.calls) {
   692  				t.Fatal("multi is full")
   693  			}
   694  			m.regions = tcase.regions
   695  
   696  			if tcase.shouldPanic {
   697  				defer func() {
   698  					if r := recover(); r == nil {
   699  						t.Error("returnResults should have panicked")
   700  					}
   701  				}()
   702  				m.returnResults(tcase.response, tcase.err)
   703  				return
   704  			}
   705  
   706  			m.returnResults(tcase.response, tcase.err)
   707  
   708  			for i, c := range tcase.calls {
   709  				expected, out := tcase.out[i], <-c.ResultChan()
   710  				if !test.ErrEqual(expected.Error, out.Error) {
   711  					t.Errorf("expected %v, got %v", expected.Error, out.Error)
   712  				}
   713  				if !proto.Equal(expected.Msg, out.Msg) {
   714  					t.Errorf("expected %v, got %v", expected.Msg, out.Msg)
   715  				}
   716  			}
   717  		})
   718  	}
   719  }
   720  
   721  func TestMultiDeserializeCellBlocks(t *testing.T) {
   722  	// TODO: add tests for panics
   723  
   724  	getCellblock := "\x00\x00\x00\x1d\x00\x00\x00\x14\x00\x00\x00\x01\x00\x05call0" +
   725  		"\x02cfa\x00\x00\x01]=\xef\x95\xd4\x04*"
   726  	getCell := &pb.Cell{
   727  		Row:       []byte("call0"),
   728  		Family:    []byte("cf"),
   729  		Qualifier: []byte("a"),
   730  		Value:     []byte{42},
   731  		Timestamp: proto.Uint64(1499982697940),
   732  		CellType:  pb.CellType(pb.CellType_PUT).Enum(),
   733  	}
   734  
   735  	appendCellblock := "\x00\x00\x00\x1e\x00\x00\x00\x14\x00\x00\x00\x02\x00\x05call1" +
   736  		"\x02cfa\x00\x00\x01]=\xef\x95\xec\x04**"
   737  	appendCell := &pb.Cell{
   738  		Row:       []byte("call1"),
   739  		Family:    []byte("cf"),
   740  		Qualifier: []byte("a"),
   741  		Value:     []byte{42, 42},
   742  		Timestamp: proto.Uint64(1499982697964),
   743  		CellType:  pb.CellType(pb.CellType_PUT).Enum(),
   744  	}
   745  
   746  	tests := []struct {
   747  		calls      []hrpc.Call
   748  		response   proto.Message
   749  		cellblocks []byte
   750  		out        *pb.MultiResponse
   751  		err        error
   752  	}{
   753  		{ // all good
   754  			calls: func() []hrpc.Call {
   755  				cs := make([]hrpc.Call, 3)
   756  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   757  				cs[0].SetRegion(reg0)
   758  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg1", "call1", nil)
   759  				cs[1].SetRegion(reg1)
   760  				cs[2], _ = hrpc.NewDelStr(context.Background(), "reg1", "call2", nil)
   761  				cs[2].SetRegion(reg1)
   762  				return cs
   763  			}(),
   764  			response: &pb.MultiResponse{
   765  				RegionActionResult: []*pb.RegionActionResult{
   766  					// reg1
   767  					&pb.RegionActionResult{
   768  						ResultOrException: []*pb.ResultOrException{
   769  							&pb.ResultOrException{ // Append
   770  								Index: proto.Uint32(2),
   771  								Result: &pb.Result{
   772  									AssociatedCellCount: proto.Int32(1),
   773  								},
   774  							},
   775  							&pb.ResultOrException{ // Delete
   776  								Index: proto.Uint32(3),
   777  								Result: &pb.Result{
   778  									AssociatedCellCount: proto.Int32(0),
   779  								},
   780  							},
   781  						},
   782  					},
   783  					// reg0
   784  					&pb.RegionActionResult{
   785  						ResultOrException: []*pb.ResultOrException{
   786  							&pb.ResultOrException{ // Get
   787  								Index: proto.Uint32(1),
   788  								Result: &pb.Result{
   789  									AssociatedCellCount: proto.Int32(1),
   790  								},
   791  							},
   792  						},
   793  					},
   794  				},
   795  			},
   796  			cellblocks: []byte(appendCellblock + getCellblock),
   797  			out: &pb.MultiResponse{
   798  				RegionActionResult: []*pb.RegionActionResult{
   799  					// reg1
   800  					&pb.RegionActionResult{
   801  						ResultOrException: []*pb.ResultOrException{
   802  							&pb.ResultOrException{ // Append
   803  								Index: proto.Uint32(2),
   804  								Result: &pb.Result{
   805  									Cell:                []*pb.Cell{appendCell},
   806  									AssociatedCellCount: proto.Int32(1),
   807  								},
   808  							},
   809  							&pb.ResultOrException{ // Delete
   810  								Index: proto.Uint32(3),
   811  								Result: &pb.Result{
   812  									AssociatedCellCount: proto.Int32(0),
   813  								},
   814  							},
   815  						},
   816  					},
   817  					// reg0
   818  					&pb.RegionActionResult{
   819  						ResultOrException: []*pb.ResultOrException{
   820  							&pb.ResultOrException{ // Get
   821  								Index: proto.Uint32(1),
   822  								Result: &pb.Result{
   823  									Cell:                []*pb.Cell{getCell},
   824  									AssociatedCellCount: proto.Int32(1),
   825  								},
   826  							},
   827  						},
   828  					},
   829  				},
   830  			},
   831  		},
   832  		{ // region exception, a result exception and an ok result
   833  			calls: func() []hrpc.Call {
   834  				cs := make([]hrpc.Call, 3)
   835  				cs[0], _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
   836  				cs[0].SetRegion(reg0)
   837  				cs[1], _ = hrpc.NewAppStr(context.Background(), "reg1", "call1", nil)
   838  				cs[1].SetRegion(reg1)
   839  				cs[2], _ = hrpc.NewDelStr(context.Background(), "reg1", "call2", nil)
   840  				cs[2].SetRegion(reg1)
   841  				return cs
   842  			}(),
   843  			response: &pb.MultiResponse{
   844  				RegionActionResult: []*pb.RegionActionResult{
   845  					// reg1
   846  					&pb.RegionActionResult{
   847  						ResultOrException: []*pb.ResultOrException{
   848  							&pb.ResultOrException{ // Append
   849  								Index: proto.Uint32(2),
   850  								Result: &pb.Result{
   851  									AssociatedCellCount: proto.Int32(1),
   852  								},
   853  							},
   854  							&pb.ResultOrException{ // Delete
   855  								Index: proto.Uint32(3),
   856  								Exception: &pb.NameBytesPair{
   857  									Name: proto.String("YOLO"), Value: []byte("SWAG")},
   858  							},
   859  						},
   860  					},
   861  					// reg0
   862  					&pb.RegionActionResult{
   863  						Exception: &pb.NameBytesPair{
   864  							Name: proto.String("YOLO"), Value: []byte("SWAG")},
   865  					},
   866  				},
   867  			},
   868  			cellblocks: []byte(appendCellblock),
   869  			out: &pb.MultiResponse{
   870  				RegionActionResult: []*pb.RegionActionResult{
   871  					// reg1
   872  					&pb.RegionActionResult{
   873  						ResultOrException: []*pb.ResultOrException{
   874  							&pb.ResultOrException{ // Append
   875  								Index: proto.Uint32(2),
   876  								Result: &pb.Result{
   877  									Cell:                []*pb.Cell{appendCell},
   878  									AssociatedCellCount: proto.Int32(1),
   879  								},
   880  							},
   881  							&pb.ResultOrException{ // Delete
   882  								Index: proto.Uint32(3),
   883  								Exception: &pb.NameBytesPair{
   884  									Name: proto.String("YOLO"), Value: []byte("SWAG")},
   885  							},
   886  						},
   887  					},
   888  					// reg0
   889  					&pb.RegionActionResult{
   890  						Exception: &pb.NameBytesPair{
   891  							Name: proto.String("YOLO"), Value: []byte("SWAG")},
   892  					},
   893  				},
   894  			},
   895  		},
   896  		{ // region exception and region results
   897  			response: &pb.MultiResponse{
   898  				RegionActionResult: []*pb.RegionActionResult{
   899  					&pb.RegionActionResult{
   900  						Exception: &pb.NameBytesPair{
   901  							Name: proto.String("YOLO"), Value: []byte("SWAG")},
   902  						ResultOrException: []*pb.ResultOrException{
   903  							&pb.ResultOrException{
   904  								Index:  proto.Uint32(2),
   905  								Result: &pb.Result{AssociatedCellCount: proto.Int32(1)},
   906  							},
   907  						},
   908  					},
   909  				},
   910  			},
   911  			err: errors.New(
   912  				"got exception for region, but still have 1 result(s) returned from it"),
   913  		},
   914  		{ // no result index
   915  			response: &pb.MultiResponse{
   916  				RegionActionResult: []*pb.RegionActionResult{
   917  					&pb.RegionActionResult{
   918  						ResultOrException: []*pb.ResultOrException{
   919  							&pb.ResultOrException{
   920  								Result: &pb.Result{AssociatedCellCount: proto.Int32(1)},
   921  							},
   922  						},
   923  					},
   924  				},
   925  			},
   926  			err: errors.New("no index for result in multi response"),
   927  		},
   928  		{ // no result and no exception
   929  			response: &pb.MultiResponse{
   930  				RegionActionResult: []*pb.RegionActionResult{
   931  					&pb.RegionActionResult{
   932  						ResultOrException: []*pb.ResultOrException{
   933  							&pb.ResultOrException{Index: proto.Uint32(2)},
   934  						},
   935  					},
   936  				},
   937  			},
   938  			err: errors.New("no result or exception for action in multi response"),
   939  		},
   940  		{ // result and exception
   941  			response: &pb.MultiResponse{
   942  				RegionActionResult: []*pb.RegionActionResult{
   943  					&pb.RegionActionResult{
   944  						ResultOrException: []*pb.ResultOrException{
   945  							&pb.ResultOrException{
   946  								Index:  proto.Uint32(2),
   947  								Result: &pb.Result{AssociatedCellCount: proto.Int32(1)},
   948  								Exception: &pb.NameBytesPair{
   949  									Name: proto.String("YOLO"), Value: []byte("SWAG")},
   950  							},
   951  						},
   952  					},
   953  				},
   954  			},
   955  			err: errors.New("got result and exception for action in multi response"),
   956  		},
   957  		{ // single call deserialize error
   958  			calls: func() []hrpc.Call {
   959  				c, _ := hrpc.NewGetStr(context.Background(), "reg0", "call0")
   960  				return []hrpc.Call{callWithCellBlocksError{c}}
   961  			}(),
   962  			response: &pb.MultiResponse{
   963  				RegionActionResult: []*pb.RegionActionResult{
   964  					&pb.RegionActionResult{
   965  						ResultOrException: []*pb.ResultOrException{
   966  							&pb.ResultOrException{
   967  								Index:  proto.Uint32(1),
   968  								Result: &pb.Result{AssociatedCellCount: proto.Int32(1)},
   969  							},
   970  						},
   971  					},
   972  				},
   973  			},
   974  			err: errors.New(
   975  				"error deserializing cellblocks for \"Get\" call as part of MultiResponse: OOPS"),
   976  		},
   977  	}
   978  
   979  	for i, tcase := range tests {
   980  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   981  			m := newMulti(1000)
   982  
   983  			if m.add(tcase.calls) {
   984  				t.Fatal("multi is full")
   985  			}
   986  
   987  			n, err := m.DeserializeCellBlocks(tcase.response, tcase.cellblocks)
   988  			if l := len(tcase.cellblocks); int(n) != l {
   989  				t.Errorf("expected read %d, got read %d", l, n)
   990  			}
   991  
   992  			if !test.ErrEqual(tcase.err, err) {
   993  				t.Fatalf("expected %v, got %v", tcase.err, err)
   994  			}
   995  
   996  			if tcase.err != nil {
   997  				return
   998  			}
   999  
  1000  			if !proto.Equal(tcase.out, tcase.response) {
  1001  				t.Fatalf("expected %v, got %v", tcase.out, tcase.response)
  1002  			}
  1003  		})
  1004  	}
  1005  }
  1006  
  1007  func BenchmarkMultiToProto(b *testing.B) {
  1008  	values := map[string]map[string][]byte{
  1009  		"cf": map[string][]byte{
  1010  			"c": []byte("v"),
  1011  		},
  1012  	}
  1013  	delValues := map[string]map[string][]byte{
  1014  		"cf": map[string][]byte{
  1015  			"c": nil,
  1016  		},
  1017  	}
  1018  
  1019  	m := newMulti(1000)
  1020  	var c hrpc.Call
  1021  	c, _ = hrpc.NewGetStr(context.Background(), "reg0", "call0")
  1022  	c.SetRegion(reg0)
  1023  	m.add([]hrpc.Call{c})
  1024  	c, _ = hrpc.NewPutStr(context.Background(), "reg0", "call1", values)
  1025  	c.SetRegion(reg0)
  1026  	m.add([]hrpc.Call{c})
  1027  	c, _ = hrpc.NewAppStr(context.Background(), "reg1", "call2", values)
  1028  	c.SetRegion(reg1)
  1029  	m.add([]hrpc.Call{c})
  1030  	c, _ = hrpc.NewDelStr(context.Background(), "reg1", "call3", delValues)
  1031  	c.SetRegion(reg1)
  1032  	m.add([]hrpc.Call{c})
  1033  	c, _ = hrpc.NewIncStr(context.Background(), "reg2", "call4", delValues)
  1034  	c.SetRegion(reg2)
  1035  	m.add([]hrpc.Call{c})
  1036  	b.Run("cellblocks", func(b *testing.B) {
  1037  		b.ReportAllocs()
  1038  		for i := 0; i < b.N; i++ {
  1039  			m.toProto(true, nil)
  1040  		}
  1041  	})
  1042  	request, _, cellblocksLen := m.toProto(true, nil)
  1043  	b.Run("cellblocks_marshalProto", func(b *testing.B) {
  1044  		b.ReportAllocs()
  1045  		for i := 0; i < b.N; i++ {
  1046  			marshalProto(m, 42, request, cellblocksLen)
  1047  		}
  1048  	})
  1049  
  1050  	b.Run("no_cellblocks", func(b *testing.B) {
  1051  		b.ReportAllocs()
  1052  		for i := 0; i < b.N; i++ {
  1053  			m.toProto(false, nil)
  1054  		}
  1055  	})
  1056  
  1057  	request, _, _ = m.toProto(false, nil)
  1058  	b.Run("no_cellblocks_marshalProto", func(b *testing.B) {
  1059  		b.ReportAllocs()
  1060  		for i := 0; i < b.N; i++ {
  1061  			marshalProto(m, 42, request, 0)
  1062  		}
  1063  	})
  1064  }
  1065  
  1066  func BenchmarkMultiToProtoLarge(b *testing.B) {
  1067  	row := "12345678901234567890123456789012345678901234567890"
  1068  	values := map[string]map[string][]byte{
  1069  		"cf": {
  1070  			"abcde":  []byte("1234567890"),
  1071  			"fghij":  []byte("1234567890"),
  1072  			"klmno":  []byte("1234567890"),
  1073  			"pqrst":  []byte("1234567890"),
  1074  			"uvxwyz": []byte("1234567890"),
  1075  		},
  1076  	}
  1077  
  1078  	regions := []hrpc.RegionInfo{
  1079  		NewInfo(0, nil, []byte("reg0"),
  1080  			[]byte("reg0,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1081  		NewInfo(1, nil, []byte("reg1"),
  1082  			[]byte("reg1,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1083  		NewInfo(2, nil, []byte("reg2"),
  1084  			[]byte("reg2,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1085  		NewInfo(3, nil, []byte("reg3"),
  1086  			[]byte("reg3,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1087  		NewInfo(4, nil, []byte("reg4"),
  1088  			[]byte("reg4,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1089  		NewInfo(5, nil, []byte("reg5"),
  1090  			[]byte("reg5,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1091  		NewInfo(6, nil, []byte("reg6"),
  1092  			[]byte("reg6,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1093  		NewInfo(7, nil, []byte("reg7"),
  1094  			[]byte("reg7,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1095  		NewInfo(8, nil, []byte("reg8"),
  1096  			[]byte("reg8,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1097  		NewInfo(9, nil, []byte("reg9"),
  1098  			[]byte("reg9,,1234567890042.56f833d5569a27c7a43fbf547b4924a4."), nil, nil),
  1099  	}
  1100  
  1101  	m := newMulti(1000)
  1102  	for i := 0; i < 1000; i++ {
  1103  		rpc, err := hrpc.NewPutStr(context.Background(), fmt.Sprintf("region%d", i%10), row, values)
  1104  		if err != nil {
  1105  			b.Fatal(err)
  1106  		}
  1107  		rpc.SetRegion(regions[i%10])
  1108  		m.add([]hrpc.Call{rpc})
  1109  	}
  1110  	b.Run("cellblocks", func(b *testing.B) {
  1111  		b.ReportAllocs()
  1112  		for i := 0; i < b.N; i++ {
  1113  			m.toProto(true, nil)
  1114  		}
  1115  	})
  1116  
  1117  	request, _, cellblocksLen := m.toProto(true, nil)
  1118  	b.Run("marshalProto", func(b *testing.B) {
  1119  		b.ReportAllocs()
  1120  		for i := 0; i < b.N; i++ {
  1121  			marshalProto(m, 42, request, cellblocksLen)
  1122  		}
  1123  	})
  1124  }