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  }