github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_revert_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/spanset" 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/storage" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/cockroach/pkg/util/log" 23 "github.com/cockroachdb/errors" 24 ) 25 26 func init() { 27 RegisterReadWriteCommand(roachpb.RevertRange, declareKeysRevertRange, RevertRange) 28 } 29 30 func declareKeysRevertRange( 31 desc *roachpb.RangeDescriptor, 32 header roachpb.Header, 33 req roachpb.Request, 34 latchSpans, lockSpans *spanset.SpanSet, 35 ) { 36 DefaultDeclareIsolatedKeys(desc, header, req, latchSpans, lockSpans) 37 // We look up the range descriptor key to check whether the span 38 // is equal to the entire range for fast stats updating. 39 latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: keys.RangeDescriptorKey(desc.StartKey)}) 40 latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: keys.RangeLastGCKey(desc.RangeID)}) 41 } 42 43 // isEmptyKeyTimeRange checks if the span has no writes in (since,until]. 44 func isEmptyKeyTimeRange( 45 readWriter storage.ReadWriter, from, to roachpb.Key, since, until hlc.Timestamp, 46 ) (bool, error) { 47 // Use a TBI to check if there is anything to delete -- the first key Seek hits 48 // may not be in the time range but the fact the TBI found any key indicates 49 // that there is *a* key in the SST that is in the time range. Thus we should 50 // proceed to iteration that actually checks timestamps on each key. 51 iter := readWriter.NewIterator(storage.IterOptions{ 52 LowerBound: from, UpperBound: to, 53 MinTimestampHint: since.Next() /* make exclusive */, MaxTimestampHint: until, 54 }) 55 defer iter.Close() 56 iter.SeekGE(storage.MVCCKey{Key: from}) 57 ok, err := iter.Valid() 58 return !ok, err 59 } 60 61 // RevertRange wipes all MVCC versions more recent than TargetTime (up to the 62 // command timestamp) of the keys covered by the specified span, adjusting the 63 // MVCC stats accordingly. 64 // 65 // Note: this should only be used when there is no user traffic writing to the 66 // target span at or above the target time. 67 func RevertRange( 68 ctx context.Context, readWriter storage.ReadWriter, cArgs CommandArgs, resp roachpb.Response, 69 ) (result.Result, error) { 70 if cArgs.Header.Txn != nil { 71 return result.Result{}, errors.New("cannot execute RevertRange within a transaction") 72 } 73 log.VEventf(ctx, 2, "RevertRange %+v", cArgs.Args) 74 75 args := cArgs.Args.(*roachpb.RevertRangeRequest) 76 reply := resp.(*roachpb.RevertRangeResponse) 77 var pd result.Result 78 79 if gc := cArgs.EvalCtx.GetGCThreshold(); args.TargetTime.LessEq(gc) { 80 return result.Result{}, errors.Errorf("cannot revert before replica GC threshold %v", gc) 81 } 82 83 if empty, err := isEmptyKeyTimeRange( 84 readWriter, args.Key, args.EndKey, args.TargetTime, cArgs.Header.Timestamp, 85 ); err != nil { 86 return result.Result{}, err 87 } else if empty { 88 log.VEventf(ctx, 2, "no keys to clear in specified time range") 89 return result.Result{}, nil 90 } 91 92 log.VEventf(ctx, 2, "clearing keys with timestamp (%v, %v]", args.TargetTime, cArgs.Header.Timestamp) 93 94 resume, err := storage.MVCCClearTimeRange(ctx, readWriter, cArgs.Stats, args.Key, args.EndKey, 95 args.TargetTime, cArgs.Header.Timestamp, cArgs.Header.MaxSpanRequestKeys) 96 if err != nil { 97 return result.Result{}, err 98 } 99 100 if resume != nil { 101 log.VEventf(ctx, 2, "hit limit while clearing keys, resume span [%v, %v)", resume.Key, resume.EndKey) 102 reply.ResumeSpan = resume 103 104 // If, and only if, we're returning a resume span do we want to return >0 105 // NumKeys. Distsender will reduce the limit for subsequent requests by the 106 // amount returned, but that doesn't really make sense for RevertRange: 107 // there isn't some combined result set size we're trying to hit across many 108 // requests; just because some earlier range ran X Clears that does not mean 109 // we want the next range to run fewer than the limit chosen for the batch 110 // size reasons. On the otherhand, we have to pass MaxKeys though if we 111 // return a resume span to cause distsender to stop after this request, as 112 // currently response combining's handling of resume spans prefers that 113 // there only be one. Thus we just set it to MaxKeys when, and only when, 114 // we're returning a ResumeSpan. 115 reply.NumKeys = cArgs.Header.MaxSpanRequestKeys 116 reply.ResumeReason = roachpb.RESUME_KEY_LIMIT 117 } 118 119 return pd, nil 120 }