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  }