github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_clear_range.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  
    16  	"github.com/cockroachdb/cockroach/pkg/keys"
    17  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result"
    18  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
    19  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset"
    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"
    24  	"github.com/cockroachdb/cockroach/pkg/util/log"
    25  	"github.com/cockroachdb/errors"
    26  	"github.com/kr/pretty"
    27  )
    28  
    29  // ClearRangeBytesThreshold is the threshold over which the ClearRange
    30  // command will use engine.ClearRange to efficiently perform a range
    31  // deletion. Otherwise, will revert to iterating through the values
    32  // and clearing them individually with engine.Clear.
    33  const ClearRangeBytesThreshold = 512 << 10 // 512KiB
    34  
    35  func init() {
    36  	RegisterReadWriteCommand(roachpb.ClearRange, declareKeysClearRange, ClearRange)
    37  }
    38  
    39  func declareKeysClearRange(
    40  	desc *roachpb.RangeDescriptor,
    41  	header roachpb.Header,
    42  	req roachpb.Request,
    43  	latchSpans, lockSpans *spanset.SpanSet,
    44  ) {
    45  	DefaultDeclareKeys(desc, header, req, latchSpans, lockSpans)
    46  	// We look up the range descriptor key to check whether the span
    47  	// is equal to the entire range for fast stats updating.
    48  	latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: keys.RangeDescriptorKey(desc.StartKey)})
    49  }
    50  
    51  // ClearRange wipes all MVCC versions of keys covered by the specified
    52  // span, adjusting the MVCC stats accordingly.
    53  //
    54  // Note that "correct" use of this command is only possible for key
    55  // spans consisting of user data that we know is not being written to
    56  // or queried any more, such as after a DROP or TRUNCATE table, or
    57  // DROP index.
    58  func ClearRange(
    59  	ctx context.Context, readWriter storage.ReadWriter, cArgs CommandArgs, resp roachpb.Response,
    60  ) (result.Result, error) {
    61  	if cArgs.Header.Txn != nil {
    62  		return result.Result{}, errors.New("cannot execute ClearRange within a transaction")
    63  	}
    64  	log.VEventf(ctx, 2, "ClearRange %+v", cArgs.Args)
    65  
    66  	// Encode MVCCKey values for start and end of clear span.
    67  	args := cArgs.Args.(*roachpb.ClearRangeRequest)
    68  	from := args.Key
    69  	to := args.EndKey
    70  	var pd result.Result
    71  
    72  	// Before clearing, compute the delta in MVCCStats.
    73  	statsDelta, err := computeStatsDelta(ctx, readWriter, cArgs, from, to)
    74  	if err != nil {
    75  		return result.Result{}, err
    76  	}
    77  	cArgs.Stats.Subtract(statsDelta)
    78  
    79  	// If the total size of data to be cleared is less than
    80  	// clearRangeBytesThreshold, clear the individual values manually,
    81  	// instead of using a range tombstone (inefficient for small ranges).
    82  	if total := statsDelta.Total(); total < ClearRangeBytesThreshold {
    83  		log.VEventf(ctx, 2, "delta=%d < threshold=%d; using non-range clear", total, ClearRangeBytesThreshold)
    84  		if err := readWriter.Iterate(from, to,
    85  			func(kv storage.MVCCKeyValue) (bool, error) {
    86  				return false, readWriter.Clear(kv.Key)
    87  			},
    88  		); err != nil {
    89  			return result.Result{}, err
    90  		}
    91  		return pd, nil
    92  	}
    93  
    94  	// Otherwise, suggest a compaction for the cleared range and clear
    95  	// the key span using engine.ClearRange.
    96  	pd.Replicated.SuggestedCompactions = []kvserverpb.SuggestedCompaction{
    97  		{
    98  			StartKey: from,
    99  			EndKey:   to,
   100  			Compaction: kvserverpb.Compaction{
   101  				Bytes:            statsDelta.Total(),
   102  				SuggestedAtNanos: cArgs.Header.Timestamp.WallTime,
   103  			},
   104  		},
   105  	}
   106  	if err := readWriter.ClearRange(storage.MakeMVCCMetadataKey(from),
   107  		storage.MakeMVCCMetadataKey(to)); err != nil {
   108  		return result.Result{}, err
   109  	}
   110  	return pd, nil
   111  }
   112  
   113  // computeStatsDelta determines the change in stats caused by the
   114  // ClearRange command. If the cleared span is the entire range,
   115  // computing MVCCStats is easy. We just negate all fields except sys
   116  // bytes and count. Note that if a race build is enabled, we use the
   117  // expectation of running in a CI environment to compute stats by
   118  // iterating over the span to provide extra verification that the fast
   119  // path of simply subtracting the non-system values is accurate.
   120  // Returns the delta stats.
   121  func computeStatsDelta(
   122  	ctx context.Context, readWriter storage.ReadWriter, cArgs CommandArgs, from, to roachpb.Key,
   123  ) (enginepb.MVCCStats, error) {
   124  	desc := cArgs.EvalCtx.Desc()
   125  	var delta enginepb.MVCCStats
   126  
   127  	// We can avoid manually computing the stats delta if we're clearing
   128  	// the entire range.
   129  	fast := desc.StartKey.Equal(from) && desc.EndKey.Equal(to)
   130  	if fast {
   131  		// Note this it is safe to use the full range MVCC stats, as
   132  		// opposed to the usual method of computing only a localizied
   133  		// stats delta, because a full-range clear prevents any concurrent
   134  		// access to the stats. Concurrent changes to range-local keys are
   135  		// explicitly ignored (i.e. SysCount, SysBytes).
   136  		delta = cArgs.EvalCtx.GetMVCCStats()
   137  		delta.SysCount, delta.SysBytes = 0, 0 // no change to system stats
   138  	}
   139  
   140  	// If we can't use the fast stats path, or race test is enabled,
   141  	// compute stats across the key span to be cleared.
   142  	if !fast || util.RaceEnabled {
   143  		iter := readWriter.NewIterator(storage.IterOptions{UpperBound: to})
   144  		computed, err := iter.ComputeStats(from, to, delta.LastUpdateNanos)
   145  		iter.Close()
   146  		if err != nil {
   147  			return enginepb.MVCCStats{}, err
   148  		}
   149  		// If we took the fast path but race is enabled, assert stats were correctly computed.
   150  		if fast {
   151  			delta.ContainsEstimates = computed.ContainsEstimates
   152  			if !delta.Equal(computed) {
   153  				log.Fatalf(ctx, "fast-path MVCCStats computation gave wrong result: diff(fast, computed) = %s",
   154  					pretty.Diff(delta, computed))
   155  			}
   156  		}
   157  		delta = computed
   158  	}
   159  
   160  	return delta, nil
   161  }