github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvclient/kvcoord/batch.go (about) 1 // Copyright 2015 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 kvcoord 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/keys" 15 "github.com/cockroachdb/cockroach/pkg/roachpb" 16 "github.com/cockroachdb/errors" 17 ) 18 19 var emptySpan = roachpb.Span{} 20 21 // truncate restricts all contained requests to the given key range and returns 22 // a new, truncated, BatchRequest. All requests contained in that batch are 23 // "truncated" to the given span, and requests which are found to not overlap 24 // the given span at all are removed. A mapping of response index to batch index 25 // is returned. For example, if 26 // 27 // ba = Put[a], Put[c], Put[b], 28 // rs = [a,bb], 29 // 30 // then truncate(ba,rs) returns a batch (Put[a], Put[b]) and positions [0,2]. 31 func truncate(ba roachpb.BatchRequest, rs roachpb.RSpan) (roachpb.BatchRequest, []int, error) { 32 truncateOne := func(args roachpb.Request) (bool, roachpb.Span, error) { 33 header := args.Header().Span() 34 if !roachpb.IsRange(args) { 35 // This is a point request. 36 if len(header.EndKey) > 0 { 37 return false, emptySpan, errors.Errorf("%T is not a range command, but EndKey is set", args) 38 } 39 keyAddr, err := keys.Addr(header.Key) 40 if err != nil { 41 return false, emptySpan, err 42 } 43 if !rs.ContainsKey(keyAddr) { 44 return false, emptySpan, nil 45 } 46 return true, header, nil 47 } 48 // We're dealing with a range-spanning request. 49 local := false 50 keyAddr, err := keys.Addr(header.Key) 51 if err != nil { 52 return false, emptySpan, err 53 } 54 endKeyAddr, err := keys.Addr(header.EndKey) 55 if err != nil { 56 return false, emptySpan, err 57 } 58 if l, r := keys.IsLocal(header.Key), keys.IsLocal(header.EndKey); l || r { 59 if !l || !r { 60 return false, emptySpan, errors.Errorf("local key mixed with global key in range") 61 } 62 local = true 63 } 64 if keyAddr.Less(rs.Key) { 65 // rs.Key can't be local because it contains range split points, which 66 // are never local. 67 if !local { 68 header.Key = rs.Key.AsRawKey() 69 } else { 70 // The local start key should be truncated to the boundary of local keys which 71 // address to rs.Key. 72 header.Key = keys.MakeRangeKeyPrefix(rs.Key) 73 } 74 } 75 if !endKeyAddr.Less(rs.EndKey) { 76 // rs.EndKey can't be local because it contains range split points, which 77 // are never local. 78 if !local { 79 header.EndKey = rs.EndKey.AsRawKey() 80 } else { 81 // The local end key should be truncated to the boundary of local keys which 82 // address to rs.EndKey. 83 header.EndKey = keys.MakeRangeKeyPrefix(rs.EndKey) 84 } 85 } 86 // Check whether the truncation has left any keys in the range. If not, 87 // we need to cut it out of the request. 88 if header.Key.Compare(header.EndKey) >= 0 { 89 return false, emptySpan, nil 90 } 91 return true, header, nil 92 } 93 94 // TODO(tschottdorf): optimize so that we don't always make a new request 95 // slice, only when something changed (copy-on-write). 96 97 var positions []int 98 truncBA := ba 99 truncBA.Requests = nil 100 for pos, arg := range ba.Requests { 101 hasRequest, newSpan, err := truncateOne(arg.GetInner()) 102 if hasRequest { 103 // Keep the old one. If we must adjust the header, must copy. 104 inner := ba.Requests[pos].GetInner() 105 oldHeader := inner.Header() 106 if newSpan.EqualValue(oldHeader.Span()) { 107 truncBA.Requests = append(truncBA.Requests, ba.Requests[pos]) 108 } else { 109 oldHeader.SetSpan(newSpan) 110 shallowCopy := inner.ShallowCopy() 111 shallowCopy.SetHeader(oldHeader) 112 truncBA.Requests = append(truncBA.Requests, roachpb.RequestUnion{}) 113 truncBA.Requests[len(truncBA.Requests)-1].MustSetInner(shallowCopy) 114 } 115 positions = append(positions, pos) 116 } 117 if err != nil { 118 return roachpb.BatchRequest{}, nil, err 119 } 120 } 121 return truncBA, positions, nil 122 } 123 124 // prev gives the right boundary of the union of all requests which don't 125 // affect keys larger than the given key. Note that a right boundary is 126 // exclusive, that is, the returned RKey is to be used as the exclusive 127 // right endpoint in finding the next range to query. 128 // 129 // Informally, a call `prev(ba, k)` means: we've already executed the parts 130 // of `ba` that intersect `[k, KeyMax)`; please tell me how far to the 131 // left the next relevant request begins. 132 // 133 // TODO(tschottdorf): again, better on BatchRequest itself, but can't pull 134 // 'keys' into 'roachpb'. 135 func prev(ba roachpb.BatchRequest, k roachpb.RKey) (roachpb.RKey, error) { 136 candidate := roachpb.RKeyMin 137 for _, union := range ba.Requests { 138 inner := union.GetInner() 139 h := inner.Header() 140 addr, err := keys.Addr(h.Key) 141 if err != nil { 142 return nil, err 143 } 144 endKey := h.EndKey 145 if len(endKey) == 0 { 146 // This is unintuitive, but if we have a point request at `x=k` then that request has 147 // already been satisfied (since the batch has already been executed for all keys `>= 148 // k`). We treat `k` as `[k,k)` which does the right thing below. It also does when `x > 149 // k` and `x < k`, so we're good. 150 // 151 // Note that if `x` is /Local/k/something, then AddrUpperBound below will turn it into 152 // `k\x00`, and so we're looking at the key range `[k, k\x00)`. This is exactly what we 153 // want since otherwise the result would be `k` and so the caller would restrict itself 154 // to `key < k`, but that excludes `k` itself and thus all local keys attached to it. 155 // 156 // See TestBatchPrevNext for a test case with commentary. 157 endKey = h.Key 158 } 159 eAddr, err := keys.AddrUpperBound(endKey) 160 if err != nil { 161 return nil, err 162 } 163 if !eAddr.Less(k) { 164 // EndKey is k or higher. 165 // [x-------y) !x.Less(k) -> skip 166 // [x-------y) !x.Less(k) -> skip 167 // [x-------y) x.Less(k) -> return k 168 // [x------y) x.Less(k) -> return k 169 // [x------y) not in this branch 170 // k 171 if addr.Less(k) { 172 // Range contains k, so won't be able to go lower. 173 // Note that in the special case in which the interval 174 // touches k, we don't take this branch. This reflects 175 // the fact that `prev(k)` means that all keys >= k have 176 // been handled, so a request `[k, x)` should simply be 177 // skipped. 178 return k, nil 179 } 180 // Range is disjoint from [KeyMin,k). 181 continue 182 } 183 // Current candidate interval is strictly to the left of `k`. 184 // We want the largest surviving candidate. 185 if candidate.Less(eAddr) { 186 candidate = eAddr 187 } 188 } 189 return candidate, nil 190 } 191 192 // next gives the left boundary of the union of all requests which don't affect 193 // keys less than the given key. Note that the left boundary is inclusive, that 194 // is, the returned RKey is the inclusive left endpoint of the keys the request 195 // should operate on next. 196 // 197 // Informally, a call `next(ba, k)` means: we've already executed the parts of 198 // `ba` that intersect `[KeyMin, k)`; please tell me how far to the right the 199 // next relevant request begins. 200 // 201 // TODO(tschottdorf): again, better on BatchRequest itself, but can't pull 202 // 'keys' into 'proto'. 203 func next(ba roachpb.BatchRequest, k roachpb.RKey) (roachpb.RKey, error) { 204 candidate := roachpb.RKeyMax 205 for _, union := range ba.Requests { 206 inner := union.GetInner() 207 h := inner.Header() 208 addr, err := keys.Addr(h.Key) 209 if err != nil { 210 return nil, err 211 } 212 if addr.Less(k) { 213 if len(h.EndKey) == 0 { 214 // `h` affects only `[KeyMin,k)`, all of which is less than `k`. 215 continue 216 } 217 eAddr, err := keys.AddrUpperBound(h.EndKey) 218 if err != nil { 219 return nil, err 220 } 221 if k.Less(eAddr) { 222 // Starts below k, but continues beyond. Need to stay at k. 223 return k, nil 224 } 225 // `h` affects only `[KeyMin,k)`, all of which is less than `k`. 226 continue 227 } 228 // We want the smallest of the surviving candidates. 229 if addr.Less(candidate) { 230 candidate = addr 231 } 232 } 233 return candidate, nil 234 }