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  }