github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/split/finder_test.go (about) 1 // Copyright 2018 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 split 12 13 import ( 14 "bytes" 15 "context" 16 "reflect" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/keys" 20 "github.com/cockroachdb/cockroach/pkg/roachpb" 21 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 22 "github.com/cockroachdb/cockroach/pkg/util/stop" 23 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 24 ) 25 26 // TestSplitFinderKey verifies the Key() method correctly 27 // finds an appropriate split point for the range. 28 func TestSplitFinderKey(t *testing.T) { 29 defer leaktest.AfterTest(t)() 30 stopper := stop.NewStopper() 31 defer stopper.Stop(context.Background()) 32 33 const ReservoirKeyOffset = 1000 34 35 // Test an empty reservoir (reservoir without load). 36 basicReservoir := [splitKeySampleSize]sample{} 37 38 // Test reservoir with no load should have no splits. 39 noLoadReservoir := [splitKeySampleSize]sample{} 40 for i := 0; i < splitKeySampleSize; i++ { 41 tempSample := sample{ 42 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 43 left: 0, 44 right: 0, 45 contained: 0, 46 } 47 noLoadReservoir[i] = tempSample 48 } 49 50 // Test a uniform reservoir. 51 uniformReservoir := [splitKeySampleSize]sample{} 52 for i := 0; i < splitKeySampleSize; i++ { 53 tempSample := sample{ 54 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 55 left: splitKeyMinCounter, 56 right: splitKeyMinCounter, 57 contained: 0, 58 } 59 uniformReservoir[i] = tempSample 60 } 61 62 // Testing a non-uniform reservoir. 63 nonUniformReservoir := [splitKeySampleSize]sample{} 64 for i := 0; i < splitKeySampleSize; i++ { 65 tempSample := sample{ 66 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 67 left: splitKeyMinCounter * i, 68 right: splitKeyMinCounter * (splitKeySampleSize - i), 69 contained: 0, 70 } 71 nonUniformReservoir[i] = tempSample 72 } 73 74 // Test a load heavy reservoir on a single hot key (the last key). 75 singleHotKeyReservoir := [splitKeySampleSize]sample{} 76 for i := 0; i < splitKeySampleSize; i++ { 77 tempSample := sample{ 78 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 79 left: 0, 80 right: splitKeyMinCounter, 81 contained: 0, 82 } 83 singleHotKeyReservoir[i] = tempSample 84 } 85 86 // Test a load heavy reservoir on multiple hot keys (first and last key). 87 multipleHotKeysReservoir := [splitKeySampleSize]sample{} 88 for i := 0; i < splitKeySampleSize; i++ { 89 tempSample := sample{ 90 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 91 left: splitKeyMinCounter, 92 right: splitKeyMinCounter, 93 contained: 0, 94 } 95 multipleHotKeysReservoir[i] = tempSample 96 } 97 multipleHotKeysReservoir[0].left = 0 98 99 // Test a spanning reservoir where splits shouldn't occur. 100 spanningReservoir := [splitKeySampleSize]sample{} 101 for i := 0; i < splitKeySampleSize; i++ { 102 tempSample := sample{ 103 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 104 left: 0, 105 right: 0, 106 contained: splitKeyMinCounter, 107 } 108 spanningReservoir[i] = tempSample 109 } 110 111 // Test that splits happen between two heavy spans. 112 multipleSpanReservoir := [splitKeySampleSize]sample{} 113 for i := 0; i < splitKeySampleSize; i++ { 114 tempSample := sample{ 115 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 116 left: splitKeyMinCounter, 117 right: splitKeyMinCounter, 118 contained: splitKeyMinCounter, 119 } 120 multipleSpanReservoir[i] = tempSample 121 } 122 midSample := sample{ 123 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + splitKeySampleSize/2)), 124 left: splitKeyMinCounter, 125 right: splitKeyMinCounter, 126 contained: 0, 127 } 128 multipleSpanReservoir[splitKeySampleSize/2] = midSample 129 130 testCases := []struct { 131 reservoir [splitKeySampleSize]sample 132 splitByLoadKey roachpb.Key 133 }{ 134 // Test an empty reservoir. 135 {basicReservoir, nil}, 136 // Test reservoir with no load should have no splits. 137 {noLoadReservoir, nil}, 138 // Test a uniform reservoir (Splits at the first key) 139 {uniformReservoir, keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset)}, 140 // Testing a non-uniform reservoir. 141 {nonUniformReservoir, keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + splitKeySampleSize/2)}, 142 // Test a load heavy reservoir on a single hot key. Splitting can't help here. 143 {singleHotKeyReservoir, nil}, 144 // Test a load heavy reservoir on multiple hot keys. Splits between the hot keys. 145 {multipleHotKeysReservoir, keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + 1)}, 146 // Test a spanning reservoir. Splitting will be bad here. Should avoid it. 147 {spanningReservoir, nil}, 148 // Test that splits happen between two heavy spans. 149 {multipleSpanReservoir, keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + splitKeySampleSize/2)}, 150 } 151 152 for i, test := range testCases { 153 finder := NewFinder(timeutil.Now()) 154 finder.samples = test.reservoir 155 if splitByLoadKey := finder.Key(); !bytes.Equal(splitByLoadKey, test.splitByLoadKey) { 156 t.Errorf( 157 "%d: expected splitByLoadKey: %v, but got splitByLoadKey: %v", 158 i, test.splitByLoadKey, splitByLoadKey) 159 } 160 } 161 } 162 163 // TestSplitFinderRecorder verifies the Record() method correctly 164 // records a span. 165 func TestSplitFinderRecorder(t *testing.T) { 166 defer leaktest.AfterTest(t)() 167 stopper := stop.NewStopper() 168 defer stopper.Stop(context.Background()) 169 170 const ReservoirKeyOffset = 1000 171 172 // getLargest is an IntN function that returns the largest number possible in [0, n) 173 getLargest := func(n int) int { 174 var result int 175 if n > 0 { 176 result = n - 1 177 } 178 return result 179 } 180 181 // getZero is an IntN function that returns 0 182 getZero := func(n int) int { return 0 } 183 184 // Test recording a key query before the reservoir is full. 185 basicReservoir := [splitKeySampleSize]sample{} 186 basicSpan := roachpb.Span{ 187 Key: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset), 188 EndKey: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + 1), 189 } 190 expectedBasicReservoir := [splitKeySampleSize]sample{} 191 expectedBasicReservoir[0] = sample{ 192 key: basicSpan.Key, 193 } 194 195 // Test recording a key query after the reservoir is full with replacement. 196 replacementReservoir := [splitKeySampleSize]sample{} 197 for i := 0; i < splitKeySampleSize; i++ { 198 tempSample := sample{ 199 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 200 left: 0, 201 right: 0, 202 contained: 0, 203 } 204 replacementReservoir[i] = tempSample 205 } 206 replacementSpan := roachpb.Span{ 207 Key: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + splitKeySampleSize), 208 EndKey: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + splitKeySampleSize + 1), 209 } 210 expectedReplacementReservoir := replacementReservoir 211 expectedReplacementReservoir[0] = sample{ 212 key: replacementSpan.Key, 213 } 214 215 // Test recording a key query after the reservoir is full without replacement. 216 fullReservoir := replacementReservoir 217 fullSpan := roachpb.Span{ 218 Key: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset), 219 EndKey: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + 1), 220 } 221 expectedFullReservoir := fullReservoir 222 for i := 0; i < splitKeySampleSize; i++ { 223 tempSample := sample{ 224 key: keys.SystemSQLCodec.TablePrefix(uint32(ReservoirKeyOffset + i)), 225 left: 1, 226 right: 0, 227 contained: 0, 228 } 229 expectedFullReservoir[i] = tempSample 230 } 231 expectedFullReservoir[0].left = 0 232 expectedFullReservoir[0].right = 1 233 234 // Test recording a spanning query. 235 spanningReservoir := replacementReservoir 236 spanningSpan := roachpb.Span{ 237 Key: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset - 1), 238 EndKey: keys.SystemSQLCodec.TablePrefix(ReservoirKeyOffset + splitKeySampleSize + 1), 239 } 240 expectedSpanningReservoir := spanningReservoir 241 for i := 0; i < splitKeySampleSize; i++ { 242 expectedSpanningReservoir[i].contained++ 243 } 244 245 testCases := []struct { 246 recordSpan roachpb.Span 247 intNFn func(int) int 248 currCount int 249 currReservoir [splitKeySampleSize]sample 250 expectedReservoir [splitKeySampleSize]sample 251 }{ 252 // Test recording a key query before the reservoir is full. 253 {basicSpan, getLargest, 0, basicReservoir, expectedBasicReservoir}, 254 // Test recording a key query after the reservoir is full with replacement. 255 {replacementSpan, getZero, splitKeySampleSize + 1, replacementReservoir, expectedReplacementReservoir}, 256 // Test recording a key query after the reservoir is full without replacement. 257 {fullSpan, getLargest, splitKeySampleSize + 1, fullReservoir, expectedFullReservoir}, 258 // Test recording a spanning query. 259 {spanningSpan, getLargest, splitKeySampleSize + 1, spanningReservoir, expectedSpanningReservoir}, 260 } 261 262 for i, test := range testCases { 263 finder := NewFinder(timeutil.Now()) 264 finder.samples = test.currReservoir 265 finder.count = test.currCount 266 finder.Record(test.recordSpan, test.intNFn) 267 if !reflect.DeepEqual(finder.samples, test.expectedReservoir) { 268 t.Errorf( 269 "%d: expected reservoir: %v, but got reservoir: %v", 270 i, test.expectedReservoir, finder.samples) 271 } 272 } 273 }