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 }