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

     1  // Copyright 2016 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 kvserverbase
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"fmt"
    17  	"time"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/keys"
    20  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
    21  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    22  	"github.com/cockroachdb/cockroach/pkg/settings"
    23  )
    24  
    25  // MergeQueueEnabled is a setting that controls whether the merge queue is
    26  // enabled.
    27  var MergeQueueEnabled = settings.RegisterBoolSetting(
    28  	"kv.range_merge.queue_enabled",
    29  	"whether the automatic merge queue is enabled",
    30  	true,
    31  )
    32  
    33  // TxnCleanupThreshold is the threshold after which a transaction is
    34  // considered abandoned and fit for removal, as measured by the
    35  // maximum of its last heartbeat and timestamp. Abort spans for the
    36  // transaction are cleaned up at the same time.
    37  //
    38  // TODO(tschottdorf): need to enforce at all times that this is much
    39  // larger than the heartbeat interval used by the coordinator.
    40  const TxnCleanupThreshold = time.Hour
    41  
    42  // CmdIDKey is a Raft command id.
    43  type CmdIDKey string
    44  
    45  // FilterArgs groups the arguments to a ReplicaCommandFilter.
    46  type FilterArgs struct {
    47  	Ctx   context.Context
    48  	CmdID CmdIDKey
    49  	Index int
    50  	Sid   roachpb.StoreID
    51  	Req   roachpb.Request
    52  	Hdr   roachpb.Header
    53  }
    54  
    55  // ProposalFilterArgs groups the arguments to ReplicaProposalFilter.
    56  type ProposalFilterArgs struct {
    57  	Ctx   context.Context
    58  	Cmd   kvserverpb.RaftCommand
    59  	CmdID CmdIDKey
    60  	Req   roachpb.BatchRequest
    61  }
    62  
    63  // ApplyFilterArgs groups the arguments to a ReplicaApplyFilter.
    64  type ApplyFilterArgs struct {
    65  	kvserverpb.ReplicatedEvalResult
    66  	CmdID   CmdIDKey
    67  	RangeID roachpb.RangeID
    68  	StoreID roachpb.StoreID
    69  }
    70  
    71  // InRaftCmd returns true if the filter is running in the context of a Raft
    72  // command (it could be running outside of one, for example for a read).
    73  func (f *FilterArgs) InRaftCmd() bool {
    74  	return f.CmdID != ""
    75  }
    76  
    77  // ReplicaRequestFilter can be used in testing to influence the error returned
    78  // from a request before it is evaluated. Return nil to continue with regular
    79  // processing or non-nil to terminate processing with the returned error.
    80  type ReplicaRequestFilter func(context.Context, roachpb.BatchRequest) *roachpb.Error
    81  
    82  // ReplicaCommandFilter may be used in tests through the StoreTestingKnobs to
    83  // intercept the handling of commands and artificially generate errors. Return
    84  // nil to continue with regular processing or non-nil to terminate processing
    85  // with the returned error.
    86  type ReplicaCommandFilter func(args FilterArgs) *roachpb.Error
    87  
    88  // ReplicaProposalFilter can be used in testing to influence the error returned
    89  // from proposals after a request is evaluated but before it is proposed.
    90  type ReplicaProposalFilter func(args ProposalFilterArgs) *roachpb.Error
    91  
    92  // A ReplicaApplyFilter can be used in testing to influence the error returned
    93  // from proposals after they apply. The returned int is treated as a
    94  // storage.proposalReevaluationReason and will only take an effect when it is
    95  // nonzero and the existing reason is zero. Similarly, the error is only applied
    96  // if there's no error so far.
    97  type ReplicaApplyFilter func(args ApplyFilterArgs) (int, *roachpb.Error)
    98  
    99  // ReplicaResponseFilter is used in unittests to modify the outbound
   100  // response returned to a waiting client after a replica command has
   101  // been processed. This filter is invoked only by the command proposer.
   102  type ReplicaResponseFilter func(context.Context, roachpb.BatchRequest, *roachpb.BatchResponse) *roachpb.Error
   103  
   104  // ReplicaRangefeedFilter is used in unit tests to modify the request, inject
   105  // responses, or return errors from rangefeeds.
   106  type ReplicaRangefeedFilter func(
   107  	args *roachpb.RangeFeedRequest, stream roachpb.Internal_RangeFeedServer,
   108  ) *roachpb.Error
   109  
   110  // ContainsKey returns whether this range contains the specified key.
   111  func ContainsKey(desc *roachpb.RangeDescriptor, key roachpb.Key) bool {
   112  	if bytes.HasPrefix(key, keys.LocalRangeIDPrefix) {
   113  		return bytes.HasPrefix(key, keys.MakeRangeIDPrefix(desc.RangeID))
   114  	}
   115  	keyAddr, err := keys.Addr(key)
   116  	if err != nil {
   117  		return false
   118  	}
   119  	return desc.ContainsKey(keyAddr)
   120  }
   121  
   122  // ContainsKeyRange returns whether this range contains the specified key range
   123  // from start to end.
   124  func ContainsKeyRange(desc *roachpb.RangeDescriptor, start, end roachpb.Key) bool {
   125  	startKeyAddr, err := keys.Addr(start)
   126  	if err != nil {
   127  		return false
   128  	}
   129  	endKeyAddr, err := keys.Addr(end)
   130  	if err != nil {
   131  		return false
   132  	}
   133  	return desc.ContainsKeyRange(startKeyAddr, endKeyAddr)
   134  }
   135  
   136  // IntersectSpan takes an span and a descriptor. It then splits the span
   137  // into up to three pieces: A first piece which is contained in the Range,
   138  // and a slice of up to two further spans which are outside of the key
   139  // range. An span for which [Key, EndKey) is empty does not result in any
   140  // spans; thus intersectIntent only applies to span ranges.
   141  //
   142  // A range-local span range is never split: It's returned as either
   143  // belonging to or outside of the descriptor's key range, and passing an
   144  // span which begins range-local but ends non-local results in a panic.
   145  //
   146  // TODO(tschottdorf): move to proto, make more gen-purpose - kv.truncate does
   147  // some similar things.
   148  func IntersectSpan(
   149  	span roachpb.Span, desc *roachpb.RangeDescriptor,
   150  ) (middle *roachpb.Span, outside []roachpb.Span) {
   151  	start, end := desc.StartKey.AsRawKey(), desc.EndKey.AsRawKey()
   152  	if len(span.EndKey) == 0 {
   153  		outside = append(outside, span)
   154  		return
   155  	}
   156  	if bytes.Compare(span.Key, keys.LocalRangeMax) < 0 {
   157  		if bytes.Compare(span.EndKey, keys.LocalRangeMax) >= 0 {
   158  			panic(fmt.Sprintf("a local intent range may not have a non-local portion: %s", span))
   159  		}
   160  		if ContainsKeyRange(desc, span.Key, span.EndKey) {
   161  			return &span, nil
   162  		}
   163  		return nil, append(outside, span)
   164  	}
   165  	// From now on, we're dealing with plain old key ranges - no more local
   166  	// addressing.
   167  	if bytes.Compare(span.Key, start) < 0 {
   168  		// Span spans a part to the left of [start, end).
   169  		iCopy := span
   170  		if bytes.Compare(start, span.EndKey) < 0 {
   171  			iCopy.EndKey = start
   172  		}
   173  		span.Key = iCopy.EndKey
   174  		outside = append(outside, iCopy)
   175  	}
   176  	if bytes.Compare(span.Key, span.EndKey) < 0 && bytes.Compare(end, span.EndKey) < 0 {
   177  		// Span spans a part to the right of [start, end).
   178  		iCopy := span
   179  		if bytes.Compare(iCopy.Key, end) < 0 {
   180  			iCopy.Key = end
   181  		}
   182  		span.EndKey = iCopy.Key
   183  		outside = append(outside, iCopy)
   184  	}
   185  	if bytes.Compare(span.Key, span.EndKey) < 0 && bytes.Compare(span.Key, start) >= 0 && bytes.Compare(end, span.EndKey) >= 0 {
   186  		middle = &span
   187  	}
   188  	return
   189  }