github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/roachpb/batch_test.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package roachpb
    12  
    13  import (
    14  	"fmt"
    15  	"reflect"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
    19  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    20  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    21  	"github.com/kr/pretty"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestBatchIsCompleteTransaction(t *testing.T) {
    26  	get := &GetRequest{}
    27  	put := &PutRequest{}
    28  	etA := &EndTxnRequest{Commit: false}
    29  	etC := &EndTxnRequest{Commit: true}
    30  	withSeq := func(r Request, s enginepb.TxnSeq) Request {
    31  		c := r.ShallowCopy()
    32  		h := c.Header()
    33  		h.Sequence = s
    34  		c.SetHeader(h)
    35  		return c
    36  	}
    37  	testCases := []struct {
    38  		reqs       []Request
    39  		isComplete bool
    40  	}{
    41  		{[]Request{get, put}, false},
    42  		{[]Request{put, get}, false},
    43  		{[]Request{etA}, false},
    44  		{[]Request{etC}, false},
    45  		{[]Request{get, etA}, false},
    46  		{[]Request{get, etC}, false},
    47  		{[]Request{put, get, etA}, false},
    48  		{[]Request{put, get, etC}, false},
    49  		{[]Request{withSeq(etA, 1)}, false},
    50  		{[]Request{withSeq(etC, 1)}, true},
    51  		{[]Request{put, withSeq(etC, 3)}, false},
    52  		{[]Request{withSeq(put, 1), withSeq(etC, 3)}, false},
    53  		{[]Request{withSeq(put, 2), withSeq(etC, 3)}, false},
    54  		{[]Request{withSeq(put, 1), withSeq(put, 2), withSeq(etA, 3)}, false},
    55  		{[]Request{withSeq(put, 1), withSeq(put, 2), withSeq(etC, 3)}, true},
    56  		{[]Request{withSeq(put, 1), withSeq(put, 2), withSeq(etC, 4)}, false},
    57  		{[]Request{withSeq(put, 1), withSeq(put, 2), withSeq(put, 3), withSeq(etA, 4)}, false},
    58  		{[]Request{withSeq(put, 1), withSeq(put, 2), withSeq(put, 3), withSeq(etC, 4)}, true},
    59  		{[]Request{withSeq(get, 0), withSeq(put, 1), withSeq(get, 1), withSeq(etC, 3)}, false},
    60  		{[]Request{withSeq(get, 0), withSeq(get, 1), withSeq(put, 2), withSeq(etC, 3)}, false},
    61  		{[]Request{withSeq(get, 0), withSeq(put, 1), withSeq(put, 2), withSeq(get, 2), withSeq(etC, 3)}, true},
    62  		{[]Request{withSeq(put, 1), withSeq(get, 1), withSeq(put, 2), withSeq(etC, 4)}, false},
    63  		{[]Request{withSeq(get, 0), withSeq(put, 1), withSeq(put, 2), withSeq(put, 3), withSeq(get, 3), withSeq(etC, 4)}, true},
    64  	}
    65  	for i, test := range testCases {
    66  		ba := BatchRequest{}
    67  		for _, args := range test.reqs {
    68  			ba.Add(args)
    69  		}
    70  		complete := ba.IsCompleteTransaction()
    71  		if complete != test.isComplete {
    72  			t.Errorf("%d: expected IsCompleteTransaction=%t, found %t", i, test.isComplete, complete)
    73  		}
    74  	}
    75  }
    76  
    77  func TestBatchSplit(t *testing.T) {
    78  	get := &GetRequest{}
    79  	scan := &ScanRequest{}
    80  	put := &PutRequest{}
    81  	spl := &AdminSplitRequest{}
    82  	dr := &DeleteRangeRequest{}
    83  	et := &EndTxnRequest{}
    84  	qi := &QueryIntentRequest{}
    85  	rv := &ReverseScanRequest{}
    86  	testCases := []struct {
    87  		reqs       []Request
    88  		sizes      []int
    89  		canSplitET bool
    90  	}{
    91  		{[]Request{get, put}, []int{1, 1}, true},
    92  		{[]Request{put, et}, []int{1, 1}, true},
    93  		{[]Request{get, get, get, put, put, get, get}, []int{3, 2, 2}, true},
    94  		{[]Request{spl, get, scan, spl, get}, []int{1, 2, 1, 1}, true},
    95  		{[]Request{spl, spl, get, spl}, []int{1, 1, 1, 1}, true},
    96  		{[]Request{get, scan, get, dr, rv, put, et}, []int{3, 1, 1, 1, 1}, true},
    97  		// Same one again, but this time don't allow EndTxn to be split.
    98  		{[]Request{get, scan, get, dr, rv, put, et}, []int{3, 1, 1, 2}, false},
    99  		// An invalid request in real life, but it demonstrates that we'll
   100  		// always split **after** an EndTxn (because either the next request
   101  		// wants to be alone, or its flags can't match the current flags, which
   102  		// have isAlone set). Could be useful if we ever want to allow executing
   103  		// multiple batches back-to-back.
   104  		{[]Request{et, scan, et}, []int{1, 2}, false},
   105  		{[]Request{et, et}, []int{1, 1}, false},
   106  		// QueryIntents count as headers that are always compatible with the
   107  		// request that follows.
   108  		{[]Request{get, qi, put}, []int{1, 2}, true},
   109  		{[]Request{get, qi, qi, qi, qi, put}, []int{1, 5}, true},
   110  		{[]Request{qi, get, qi, get, qi, get, qi, put, qi, put, qi, get, qi, get}, []int{6, 4, 4}, true},
   111  		{[]Request{qi, spl, qi, get, scan, qi, qi, spl, qi, get}, []int{1, 1, 5, 1, 2}, true},
   112  		{[]Request{scan, qi, qi, qi, et}, []int{4, 1}, true},
   113  		{[]Request{scan, qi, qi, qi, et}, []int{5}, false},
   114  		{[]Request{put, qi, qi, qi, et}, []int{1, 3, 1}, true},
   115  		{[]Request{put, qi, qi, qi, et}, []int{5}, false},
   116  	}
   117  
   118  	for i, test := range testCases {
   119  		ba := BatchRequest{}
   120  		for _, args := range test.reqs {
   121  			ba.Add(args)
   122  		}
   123  		var partLen []int
   124  		var recombined []RequestUnion
   125  		for _, part := range ba.Split(test.canSplitET) {
   126  			recombined = append(recombined, part...)
   127  			partLen = append(partLen, len(part))
   128  		}
   129  		if !reflect.DeepEqual(partLen, test.sizes) {
   130  			t.Errorf("%d: expected chunks %v, got %v", i, test.sizes, partLen)
   131  		}
   132  		if !reflect.DeepEqual(recombined, ba.Requests) {
   133  			t.Errorf("%d: started with:\n%+v\ngot back:\n%+v", i, ba.Requests, recombined)
   134  		}
   135  	}
   136  }
   137  
   138  func TestBatchRequestGetArg(t *testing.T) {
   139  	get := RequestUnion{
   140  		Value: &RequestUnion_Get{Get: &GetRequest{}},
   141  	}
   142  	end := RequestUnion{
   143  		Value: &RequestUnion_EndTxn{EndTxn: &EndTxnRequest{}},
   144  	}
   145  	testCases := []struct {
   146  		bu         []RequestUnion
   147  		expB, expG bool
   148  	}{
   149  		{[]RequestUnion{}, false, false},
   150  		{[]RequestUnion{get}, false, true},
   151  		{[]RequestUnion{end, get}, false, true},
   152  		{[]RequestUnion{get, end}, true, true},
   153  	}
   154  
   155  	for i, c := range testCases {
   156  		br := BatchRequest{Requests: c.bu}
   157  		if _, r := br.GetArg(EndTxn); r != c.expB {
   158  			t.Errorf("%d: unexpected batch request for %v: %v", i, c.bu, r)
   159  		}
   160  		if _, r := br.GetArg(Get); r != c.expG {
   161  			t.Errorf("%d: unexpected get match for %v: %v", i, c.bu, r)
   162  		}
   163  
   164  	}
   165  }
   166  
   167  func TestBatchRequestSummary(t *testing.T) {
   168  	// The Summary function is generated automatically, so the tests don't need to
   169  	// be exhaustive.
   170  	testCases := []struct {
   171  		reqs     []Request
   172  		expected string
   173  	}{
   174  		{
   175  			reqs:     []Request{},
   176  			expected: "empty batch",
   177  		},
   178  		{
   179  			reqs:     []Request{&GetRequest{}},
   180  			expected: "1 Get",
   181  		},
   182  		{
   183  			reqs:     []Request{&PutRequest{}},
   184  			expected: "1 Put",
   185  		},
   186  		{
   187  			reqs:     []Request{&ConditionalPutRequest{}},
   188  			expected: "1 CPut",
   189  		},
   190  		{
   191  			reqs:     []Request{&ReverseScanRequest{}},
   192  			expected: "1 RevScan",
   193  		},
   194  		{
   195  			reqs: []Request{
   196  				&GetRequest{}, &GetRequest{}, &PutRequest{}, &ScanRequest{}, &ScanRequest{},
   197  			},
   198  			expected: "2 Get, 1 Put, 2 Scan",
   199  		},
   200  		{
   201  			reqs: []Request{
   202  				&CheckConsistencyRequest{}, &InitPutRequest{}, &TruncateLogRequest{},
   203  			},
   204  			expected: "1 TruncLog, 1 ChkConsistency, 1 InitPut",
   205  		},
   206  	}
   207  	for i, tc := range testCases {
   208  		var br BatchRequest
   209  		for _, v := range tc.reqs {
   210  			var ru RequestUnion
   211  			ru.MustSetInner(v)
   212  			br.Requests = append(br.Requests, ru)
   213  		}
   214  		if str := br.Summary(); str != tc.expected {
   215  			t.Errorf("%d: got '%s', expected '%s', batch: %+v", i, str, tc.expected, br)
   216  		}
   217  	}
   218  }
   219  
   220  func TestLockSpanIterate(t *testing.T) {
   221  	type testReq struct {
   222  		req    Request
   223  		resp   Response
   224  		span   Span
   225  		resume Span
   226  	}
   227  	testReqs := []testReq{
   228  		{&ScanRequest{}, &ScanResponse{}, sp("a", "c"), sp("b", "c")},
   229  		{&ReverseScanRequest{}, &ReverseScanResponse{}, sp("d", "f"), sp("d", "e")},
   230  		{&PutRequest{}, &PutResponse{}, sp("m", ""), sp("", "")},
   231  		{&DeleteRangeRequest{}, &DeleteRangeResponse{}, sp("n", "p"), sp("o", "p")},
   232  		{&ScanRequest{KeyLocking: lock.Exclusive}, &ScanResponse{}, sp("g", "i"), sp("h", "i")},
   233  		{&ReverseScanRequest{KeyLocking: lock.Exclusive}, &ReverseScanResponse{}, sp("j", "l"), sp("k", "l")},
   234  	}
   235  
   236  	// NB: can't import testutils for RunTrueAndFalse.
   237  	for _, resume := range []bool{false, true} {
   238  		t.Run(fmt.Sprintf("resume=%t", resume), func(t *testing.T) {
   239  			// A batch request with a batch response with no ResumeSpan.
   240  			ba := BatchRequest{}
   241  			br := BatchResponse{}
   242  			for i := range testReqs {
   243  				tr := &testReqs[i]
   244  				tr.req.SetHeader(RequestHeaderFromSpan(tr.span))
   245  				ba.Add(tr.req)
   246  				if resume {
   247  					tr.resp.SetHeader(ResponseHeader{ResumeSpan: &tr.resume})
   248  				}
   249  				br.Add(tr.resp)
   250  			}
   251  
   252  			var spans [lock.MaxDurability + 1][]Span
   253  			fn := func(span Span, dur lock.Durability) {
   254  				spans[dur] = append(spans[dur], span)
   255  			}
   256  			ba.LockSpanIterate(&br, fn)
   257  
   258  			toExpSpans := func(trs ...testReq) []Span {
   259  				exp := make([]Span, len(trs))
   260  				for i, tr := range trs {
   261  					exp[i] = tr.span
   262  					if resume {
   263  						exp[i].EndKey = tr.resume.Key
   264  					}
   265  				}
   266  				return exp
   267  			}
   268  
   269  			// The intent writes are replicated locking request.
   270  			require.Equal(t, toExpSpans(testReqs[2], testReqs[3]), spans[lock.Replicated])
   271  
   272  			// The scans with KeyLocking are unreplicated locking requests.
   273  			require.Equal(t, toExpSpans(testReqs[4], testReqs[5]), spans[lock.Unreplicated])
   274  		})
   275  	}
   276  }
   277  
   278  func TestRefreshSpanIterate(t *testing.T) {
   279  	testCases := []struct {
   280  		req    Request
   281  		resp   Response
   282  		span   Span
   283  		resume Span
   284  	}{
   285  		{&ConditionalPutRequest{}, &ConditionalPutResponse{}, sp("a", ""), Span{}},
   286  		{&PutRequest{}, &PutResponse{}, sp("a-put", ""), Span{}},
   287  		{&InitPutRequest{}, &InitPutResponse{}, sp("a-initput", ""), Span{}},
   288  		{&IncrementRequest{}, &IncrementResponse{}, sp("a-inc", ""), Span{}},
   289  		{&ScanRequest{}, &ScanResponse{}, sp("a", "c"), sp("b", "c")},
   290  		{&GetRequest{}, &GetResponse{}, sp("b", ""), Span{}},
   291  		{&ReverseScanRequest{}, &ReverseScanResponse{}, sp("d", "f"), sp("d", "e")},
   292  		{&DeleteRangeRequest{}, &DeleteRangeResponse{}, sp("g", "i"), sp("h", "i")},
   293  	}
   294  
   295  	// A batch request with a batch response with no ResumeSpan.
   296  	ba := BatchRequest{}
   297  	br := BatchResponse{}
   298  	for _, tc := range testCases {
   299  		tc.req.SetHeader(RequestHeaderFromSpan(tc.span))
   300  		ba.Add(tc.req)
   301  		br.Add(tc.resp)
   302  	}
   303  
   304  	var readSpans []Span
   305  	fn := func(span Span) {
   306  		readSpans = append(readSpans, span)
   307  	}
   308  	ba.RefreshSpanIterate(&br, fn)
   309  	// The conditional put and init put are not considered read spans.
   310  	expReadSpans := []Span{testCases[4].span, testCases[5].span, testCases[6].span, testCases[7].span}
   311  	require.Equal(t, expReadSpans, readSpans)
   312  
   313  	// Batch responses with ResumeSpans.
   314  	ba = BatchRequest{}
   315  	br = BatchResponse{}
   316  	for _, tc := range testCases {
   317  		tc.req.SetHeader(RequestHeaderFromSpan(tc.span))
   318  		ba.Add(tc.req)
   319  		if tc.resume.Key != nil {
   320  			resume := tc.resume
   321  			tc.resp.SetHeader(ResponseHeader{ResumeSpan: &resume})
   322  		}
   323  		br.Add(tc.resp)
   324  	}
   325  
   326  	readSpans = []Span{}
   327  	ba.RefreshSpanIterate(&br, fn)
   328  	expReadSpans = []Span{
   329  		sp("a", "b"),
   330  		sp("b", ""),
   331  		sp("e", "f"),
   332  		sp("g", "h"),
   333  	}
   334  	require.Equal(t, expReadSpans, readSpans)
   335  }
   336  
   337  func TestBatchResponseCombine(t *testing.T) {
   338  	br := &BatchResponse{}
   339  	{
   340  		txn := MakeTransaction(
   341  			"test", nil /* baseKey */, NormalUserPriority,
   342  			hlc.Timestamp{WallTime: 123}, 0, /* maxOffsetNs */
   343  		)
   344  		brTxn := &BatchResponse{
   345  			BatchResponse_Header: BatchResponse_Header{
   346  				Txn: &txn,
   347  			},
   348  		}
   349  		if err := br.Combine(brTxn, nil); err != nil {
   350  			t.Fatal(err)
   351  		}
   352  		if br.Txn.Name != "test" {
   353  			t.Fatal("Combine() did not update the header")
   354  		}
   355  	}
   356  
   357  	br.Responses = make([]ResponseUnion, 1)
   358  
   359  	singleScanBR := func() *BatchResponse {
   360  		var union ResponseUnion
   361  		union.MustSetInner(&ScanResponse{
   362  			Rows: []KeyValue{{
   363  				Key: Key("bar"),
   364  			}},
   365  			IntentRows: []KeyValue{{
   366  				Key: Key("baz"),
   367  			}},
   368  		})
   369  		return &BatchResponse{
   370  			Responses: []ResponseUnion{union},
   371  		}
   372  	}
   373  	// Combine twice with a single scan result: first one should simply copy over, second
   374  	// one should add on top. Slightly different code paths internally, hence the distinction.
   375  	for i := 0; i < 2; i++ {
   376  		if err := br.Combine(singleScanBR(), []int{0}); err != nil {
   377  			t.Fatal(err)
   378  		}
   379  		scan := br.Responses[0].GetInner().(*ScanResponse)
   380  		if exp := i + 1; len(scan.Rows) != exp {
   381  			t.Fatalf("expected %d rows, got %+v", exp, br)
   382  		}
   383  		if exp := i + 1; len(scan.IntentRows) != exp {
   384  			t.Fatalf("expected %d intent rows, got %+v", exp, br)
   385  		}
   386  	}
   387  
   388  	var union ResponseUnion
   389  	union.MustSetInner(&PutResponse{})
   390  	br.Responses = []ResponseUnion{union, br.Responses[0]}
   391  
   392  	// Now we have br = [Put, Scan]. Combine should use the position to
   393  	// combine singleScanBR on top of the Scan at index one.
   394  	if err := br.Combine(singleScanBR(), []int{1}); err != nil {
   395  		t.Fatal(err)
   396  	}
   397  	scan := br.Responses[1].GetInner().(*ScanResponse)
   398  	expRows := 3
   399  	if len(scan.Rows) != expRows {
   400  		t.Fatalf("expected %d rows, got %s", expRows, pretty.Sprint(scan))
   401  	}
   402  	if len(scan.IntentRows) != expRows {
   403  		t.Fatalf("expected %d intent rows, got %s", expRows, pretty.Sprint(scan))
   404  	}
   405  	if err := br.Combine(singleScanBR(), []int{0}); err.Error() !=
   406  		`can not combine *roachpb.PutResponse and *roachpb.ScanResponse` {
   407  		t.Fatal(err)
   408  	}
   409  }
   410  
   411  func sp(start, end string) Span {
   412  	res := Span{Key: Key(start)}
   413  	if end != "" {
   414  		res.EndKey = Key(end)
   415  	}
   416  	return res
   417  }