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 }