github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvclient/kvcoord/truncate_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 kvcoord
    12  
    13  import (
    14  	"bytes"
    15  	"reflect"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/keys"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    21  	"github.com/cockroachdb/cockroach/pkg/testutils"
    22  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    23  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    24  )
    25  
    26  func TestTruncate(t *testing.T) {
    27  	defer leaktest.AfterTest(t)()
    28  	loc := func(s string) string {
    29  		return string(keys.RangeDescriptorKey(roachpb.RKey(s)))
    30  	}
    31  	locPrefix := func(s string) string {
    32  		return string(keys.MakeRangeKeyPrefix(roachpb.RKey(s)))
    33  	}
    34  	testCases := []struct {
    35  		keys     [][2]string
    36  		expKeys  [][2]string
    37  		from, to string
    38  		desc     [2]string // optional, defaults to {from,to}
    39  		err      string
    40  	}{
    41  		{
    42  			// Keys inside of active range.
    43  			keys:    [][2]string{{"a", "q"}, {"c"}, {"b, e"}, {"q"}},
    44  			expKeys: [][2]string{{"a", "q"}, {"c"}, {"b, e"}, {"q"}},
    45  			from:    "a", to: "q\x00",
    46  		},
    47  		{
    48  			// Keys outside of active range.
    49  			keys:    [][2]string{{"a"}, {"a", "b"}, {"q"}, {"q", "z"}},
    50  			expKeys: [][2]string{{}, {}, {}, {}},
    51  			from:    "b", to: "q",
    52  		},
    53  		{
    54  			// Range-local keys inside of active range.
    55  			keys:    [][2]string{{loc("b")}, {loc("c")}},
    56  			expKeys: [][2]string{{loc("b")}, {loc("c")}},
    57  			from:    "b", to: "e",
    58  		},
    59  		{
    60  			// Range-local key outside of active range.
    61  			keys:    [][2]string{{loc("a")}},
    62  			expKeys: [][2]string{{}},
    63  			from:    "b", to: "e",
    64  		},
    65  		{
    66  			// Range-local range contained in active range.
    67  			keys:    [][2]string{{loc("b"), loc("e") + "\x00"}},
    68  			expKeys: [][2]string{{loc("b"), loc("e") + "\x00"}},
    69  			from:    "b", to: "e\x00",
    70  		},
    71  		{
    72  			// Range-local range not contained in active range.
    73  			keys:    [][2]string{{loc("a"), loc("b")}},
    74  			expKeys: [][2]string{{}},
    75  			from:    "c", to: "e",
    76  		},
    77  		{
    78  			// Range-local range not contained in active range.
    79  			keys:    [][2]string{{loc("a"), locPrefix("b")}, {loc("e"), loc("f")}},
    80  			expKeys: [][2]string{{}, {}},
    81  			from:    "b", to: "e",
    82  		},
    83  		{
    84  			// Range-local range partially contained in active range.
    85  			keys:    [][2]string{{loc("a"), loc("b")}},
    86  			expKeys: [][2]string{{loc("a"), locPrefix("b")}},
    87  			from:    "a", to: "b",
    88  		},
    89  		{
    90  			// Range-local range partially contained in active range.
    91  			keys:    [][2]string{{loc("a"), loc("b")}},
    92  			expKeys: [][2]string{{locPrefix("b"), loc("b")}},
    93  			from:    "b", to: "e",
    94  		},
    95  		{
    96  			// Range-local range contained in active range.
    97  			keys:    [][2]string{{locPrefix("b"), loc("b")}},
    98  			expKeys: [][2]string{{locPrefix("b"), loc("b")}},
    99  			from:    "b", to: "c",
   100  		},
   101  		{
   102  			// Mixed range-local vs global key range.
   103  			keys: [][2]string{{loc("c"), "d\x00"}},
   104  			from: "b", to: "e",
   105  			err: "local key mixed with global key",
   106  		},
   107  		{
   108  			// Key range touching and intersecting active range.
   109  			keys:    [][2]string{{"a", "b"}, {"a", "c"}, {"p", "q"}, {"p", "r"}, {"a", "z"}},
   110  			expKeys: [][2]string{{"b", "c"}, {"p", "q"}, {"p", "q"}, {"b", "q"}},
   111  			from:    "b", to: "q",
   112  		},
   113  		// Active key range is intersection of descriptor and [from,to).
   114  		{
   115  			keys:    [][2]string{{"c", "q"}},
   116  			expKeys: [][2]string{{"d", "p"}},
   117  			from:    "a", to: "z",
   118  			desc: [2]string{"d", "p"},
   119  		},
   120  		{
   121  			keys:    [][2]string{{"c", "q"}},
   122  			expKeys: [][2]string{{"d", "p"}},
   123  			from:    "d", to: "p",
   124  			desc: [2]string{"a", "z"},
   125  		},
   126  	}
   127  
   128  	for i, test := range testCases {
   129  		goldenOriginal := roachpb.BatchRequest{}
   130  		for _, ks := range test.keys {
   131  			if len(ks[1]) > 0 {
   132  				goldenOriginal.Add(&roachpb.ResolveIntentRangeRequest{
   133  					RequestHeader: roachpb.RequestHeader{
   134  						Key: roachpb.Key(ks[0]), EndKey: roachpb.Key(ks[1]),
   135  					},
   136  					IntentTxn: enginepb.TxnMeta{ID: uuid.MakeV4()},
   137  				})
   138  			} else {
   139  				goldenOriginal.Add(&roachpb.GetRequest{
   140  					RequestHeader: roachpb.RequestHeader{Key: roachpb.Key(ks[0])},
   141  				})
   142  			}
   143  		}
   144  
   145  		original := roachpb.BatchRequest{Requests: make([]roachpb.RequestUnion, len(goldenOriginal.Requests))}
   146  		for i, request := range goldenOriginal.Requests {
   147  			original.Requests[i].MustSetInner(request.GetInner().ShallowCopy())
   148  		}
   149  
   150  		desc := &roachpb.RangeDescriptor{
   151  			StartKey: roachpb.RKey(test.desc[0]), EndKey: roachpb.RKey(test.desc[1]),
   152  		}
   153  		if len(desc.StartKey) == 0 {
   154  			desc.StartKey = roachpb.RKey(test.from)
   155  		}
   156  		if len(desc.EndKey) == 0 {
   157  			desc.EndKey = roachpb.RKey(test.to)
   158  		}
   159  		rs := roachpb.RSpan{Key: roachpb.RKey(test.from), EndKey: roachpb.RKey(test.to)}
   160  		rs, err := rs.Intersect(desc)
   161  		if err != nil {
   162  			t.Errorf("%d: intersection failure: %v", i, err)
   163  			continue
   164  		}
   165  		ba, pos, err := truncate(original, rs)
   166  		if err != nil || test.err != "" {
   167  			if !testutils.IsError(err, test.err) {
   168  				t.Errorf("%d: %v (expected: %q)", i, err, test.err)
   169  			}
   170  			continue
   171  		}
   172  		var reqs int
   173  		for j, arg := range ba.Requests {
   174  			req := arg.GetInner()
   175  			if h := req.Header(); !bytes.Equal(h.Key, roachpb.Key(test.expKeys[j][0])) || !bytes.Equal(h.EndKey, roachpb.Key(test.expKeys[j][1])) {
   176  				t.Errorf("%d.%d: range mismatch: actual [%q,%q), wanted [%q,%q)", i, j,
   177  					h.Key, h.EndKey, test.expKeys[j][0], test.expKeys[j][1])
   178  			} else if len(h.Key) != 0 {
   179  				reqs++
   180  			}
   181  		}
   182  		if num := len(pos); reqs != num {
   183  			t.Errorf("%d: counted %d requests, but truncation indicated %d", i, reqs, num)
   184  		}
   185  		if !reflect.DeepEqual(original, goldenOriginal) {
   186  			t.Errorf("%d: truncation mutated original:\nexpected: %s\nactual: %s",
   187  				i, goldenOriginal, original)
   188  		}
   189  	}
   190  }