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 }