github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_clear_range_test.go (about)

     1  // Copyright 2017 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 batcheval
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    21  	"github.com/cockroachdb/cockroach/pkg/storage"
    22  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    23  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    24  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    25  	"github.com/cockroachdb/errors"
    26  )
    27  
    28  type wrappedBatch struct {
    29  	storage.Batch
    30  	clearCount      int
    31  	clearRangeCount int
    32  }
    33  
    34  func (wb *wrappedBatch) Clear(key storage.MVCCKey) error {
    35  	wb.clearCount++
    36  	return wb.Batch.Clear(key)
    37  }
    38  
    39  func (wb *wrappedBatch) ClearRange(start, end storage.MVCCKey) error {
    40  	wb.clearRangeCount++
    41  	return wb.Batch.ClearRange(start, end)
    42  }
    43  
    44  // TestCmdClearRangeBytesThreshold verifies that clear range resorts to
    45  // clearing keys individually if under the bytes threshold and issues a
    46  // clear range command to the batch otherwise.
    47  func TestCmdClearRangeBytesThreshold(t *testing.T) {
    48  	defer leaktest.AfterTest(t)()
    49  
    50  	startKey := roachpb.Key("0000")
    51  	endKey := roachpb.Key("9999")
    52  	desc := roachpb.RangeDescriptor{
    53  		RangeID:  99,
    54  		StartKey: roachpb.RKey(startKey),
    55  		EndKey:   roachpb.RKey(endKey),
    56  	}
    57  	valueStr := strings.Repeat("0123456789", 1024)
    58  	var value roachpb.Value
    59  	value.SetString(valueStr) // 10KiB
    60  	halfFull := ClearRangeBytesThreshold / (2 * len(valueStr))
    61  	overFull := ClearRangeBytesThreshold/len(valueStr) + 1
    62  	tests := []struct {
    63  		keyCount           int
    64  		expClearCount      int
    65  		expClearRangeCount int
    66  	}{
    67  		{
    68  			keyCount:           1,
    69  			expClearCount:      1,
    70  			expClearRangeCount: 0,
    71  		},
    72  		// More than a single key, but not enough to use ClearRange.
    73  		{
    74  			keyCount:           halfFull,
    75  			expClearCount:      halfFull,
    76  			expClearRangeCount: 0,
    77  		},
    78  		// With key sizes requiring additional space, this will overshoot.
    79  		{
    80  			keyCount:           overFull,
    81  			expClearCount:      0,
    82  			expClearRangeCount: 1,
    83  		},
    84  	}
    85  
    86  	for _, test := range tests {
    87  		t.Run("", func(t *testing.T) {
    88  			ctx := context.Background()
    89  			eng := storage.NewDefaultInMem()
    90  			defer eng.Close()
    91  
    92  			var stats enginepb.MVCCStats
    93  			for i := 0; i < test.keyCount; i++ {
    94  				key := roachpb.Key(fmt.Sprintf("%04d", i))
    95  				if err := storage.MVCCPut(ctx, eng, &stats, key, hlc.Timestamp{WallTime: int64(i % 2)}, value, nil); err != nil {
    96  					t.Fatal(err)
    97  				}
    98  			}
    99  
   100  			batch := &wrappedBatch{Batch: eng.NewBatch()}
   101  			defer batch.Close()
   102  
   103  			var h roachpb.Header
   104  			h.RangeID = desc.RangeID
   105  
   106  			cArgs := CommandArgs{Header: h}
   107  			cArgs.EvalCtx = (&MockEvalCtx{Desc: &desc, Clock: hlc.NewClock(hlc.UnixNano, time.Nanosecond), Stats: stats}).EvalContext()
   108  			cArgs.Args = &roachpb.ClearRangeRequest{
   109  				RequestHeader: roachpb.RequestHeader{
   110  					Key:    startKey,
   111  					EndKey: endKey,
   112  				},
   113  			}
   114  			cArgs.Stats = &enginepb.MVCCStats{}
   115  
   116  			if _, err := ClearRange(ctx, batch, cArgs, &roachpb.ClearRangeResponse{}); err != nil {
   117  				t.Fatal(err)
   118  			}
   119  
   120  			// Verify cArgs.Stats is equal to the stats we wrote.
   121  			newStats := stats
   122  			newStats.SysBytes, newStats.SysCount = 0, 0       // ignore these values
   123  			cArgs.Stats.SysBytes, cArgs.Stats.SysCount = 0, 0 // these too, as GC threshold is updated
   124  			newStats.Add(*cArgs.Stats)
   125  			newStats.AgeTo(0) // pin at LastUpdateNanos==0
   126  			if !newStats.Equal(enginepb.MVCCStats{}) {
   127  				t.Errorf("expected stats on original writes to be negated on clear range: %+v vs %+v", stats, *cArgs.Stats)
   128  			}
   129  
   130  			// Verify we see the correct counts for Clear and ClearRange.
   131  			if a, e := batch.clearCount, test.expClearCount; a != e {
   132  				t.Errorf("expected %d clears; got %d", e, a)
   133  			}
   134  			if a, e := batch.clearRangeCount, test.expClearRangeCount; a != e {
   135  				t.Errorf("expected %d clear ranges; got %d", e, a)
   136  			}
   137  
   138  			// Now ensure that the data is gone, whether it was a ClearRange or individual calls to clear.
   139  			if err := batch.Commit(true /* commit */); err != nil {
   140  				t.Fatal(err)
   141  			}
   142  			if err := eng.Iterate(startKey, endKey,
   143  				func(kv storage.MVCCKeyValue) (bool, error) {
   144  					return true, errors.New("expected no data in underlying engine")
   145  				},
   146  			); err != nil {
   147  				t.Fatal(err)
   148  			}
   149  		})
   150  	}
   151  }