// prev gives the right boundary of the union of all requests which don't // affect keys larger than the given key. // TODO(tschottdorf): again, better on BatchRequest itself, but can't pull // 'keys' into 'proto'. func prev(ba proto.BatchRequest, k proto.Key) proto.Key { candidate := proto.KeyMin for _, union := range ba.Requests { h := union.GetValue().(proto.Request).Header() addr := keys.KeyAddress(h.Key) eAddr := keys.KeyAddress(h.EndKey) if len(eAddr) == 0 { // Can probably avoid having to compute Next() here if // we're in the mood for some more complexity. eAddr = addr.Next() } if !eAddr.Less(k) { if !k.Less(addr) { // Range contains k, so won't be able to go lower. return k } // Range is disjoint from [KeyMin,k). continue } // We want the largest surviving candidate. if candidate.Less(addr) { candidate = addr } } return candidate }
// clearOverlappingCachedRangeDescriptors looks up and clears any // cache entries which overlap the specified key or descriptor. func (rdc *rangeDescriptorCache) clearOverlappingCachedRangeDescriptors(key, metaKey proto.Key, desc *proto.RangeDescriptor) { if desc.StartKey.Equal(desc.EndKey) { // True for some unittests. return } // Clear out any descriptors which subsume the key which we're going // to cache. For example, if an existing KeyMin->KeyMax descriptor // should be cleared out in favor of a KeyMin->"m" descriptor. k, v, ok := rdc.rangeCache.Ceil(rangeCacheKey(metaKey)) if ok { descriptor := v.(*proto.RangeDescriptor) addrKey := keys.KeyAddress(key) if !addrKey.Less(descriptor.StartKey) && !descriptor.EndKey.Less(addrKey) { if log.V(1) { log.Infof("clearing overlapping descriptor: key=%s desc=%s", k, descriptor) } rdc.rangeCache.Del(k.(rangeCacheKey)) } } // Also clear any descriptors which are subsumed by the one we're // going to cache. This could happen on a merge (and also happens // when there's a lot of concurrency). Iterate from StartKey.Next(). rdc.rangeCache.DoRange(func(k, v interface{}) { if log.V(1) { log.Infof("clearing subsumed descriptor: key=%s desc=%s", k, v.(*proto.RangeDescriptor)) } rdc.rangeCache.Del(k.(rangeCacheKey)) }, rangeCacheKey(keys.RangeMetaKey(desc.StartKey.Next())), rangeCacheKey(keys.RangeMetaKey(desc.EndKey))) }
func doLookup(t *testing.T, rc *rangeDescriptorCache, key string) *proto.RangeDescriptor { r, err := rc.LookupRangeDescriptor(proto.Key(key), lookupOptions{}) if err != nil { t.Fatalf("Unexpected error from LookupRangeDescriptor: %s", err.Error()) } if !r.ContainsKey(keys.KeyAddress(proto.Key(key))) { t.Fatalf("Returned range did not contain key: %s-%s, %s", r.StartKey, r.EndKey, key) } log.Infof("doLookup: %s %+v", key, r) return r }
// next gives the left boundary of the union of all requests which don't // affect keys less than the given key. // TODO(tschottdorf): again, better on BatchRequest itself, but can't pull // 'keys' into 'proto'. func next(ba proto.BatchRequest, k proto.Key) proto.Key { candidate := proto.KeyMax for _, union := range ba.Requests { h := union.GetValue().(proto.Request).Header() addr := keys.KeyAddress(h.Key) if addr.Less(k) { if eAddr := keys.KeyAddress(h.EndKey); k.Less(eAddr) { // Starts below k, but continues beyond. Need to stay at k. return k } // Affects only [KeyMin,k). continue } // We want the smallest of the surviving candidates. if addr.Less(candidate) { candidate = addr } } return candidate }
// maybeBeginTxn begins a new transaction if a txn has been specified // in the request but has a nil ID. The new transaction is initialized // using the name and isolation in the otherwise uninitialized txn. // The Priority, if non-zero is used as a minimum. func (tc *TxnCoordSender) maybeBeginTxn(header *proto.RequestHeader) { if header.Txn != nil { if len(header.Txn.ID) == 0 { newTxn := proto.NewTransaction(header.Txn.Name, keys.KeyAddress(header.Key), header.GetUserPriority(), header.Txn.Isolation, tc.clock.Now(), tc.clock.MaxOffset().Nanoseconds()) // Use existing priority as a minimum. This is used on transaction // aborts to ratchet priority when creating successor transaction. if newTxn.Priority < header.Txn.Priority { newTxn.Priority = header.Txn.Priority } header.Txn = newTxn } } }
func TestKeyAddress(t *testing.T) { defer leaktest.AfterTest(t) testCases := []struct { key, expAddress proto.Key }{ {MakeNameMetadataKey(0, "foo"), proto.Key("\x00name-\bfoo")}, {MakeNameMetadataKey(0, "BAR"), proto.Key("\x00name-\bbar")}, {MakeDescMetadataKey(123), proto.Key("\x00desc-\t{")}, } for i, test := range testCases { result := keys.KeyAddress(test.key) if !result.Equal(test.expAddress) { t.Errorf("%d: expected address for key %q doesn't match %q", i, test.key, test.expAddress) } } }
// maybeBeginTxn begins a new transaction if a txn has been specified // in the request but has a nil ID. The new transaction is initialized // using the name and isolation in the otherwise uninitialized txn. // The Priority, if non-zero is used as a minimum. func (tc *TxnCoordSender) maybeBeginTxn(ba *proto.BatchRequest) { if ba.Txn == nil { return } if len(ba.Requests) == 0 { panic("empty batch with txn") } if len(ba.Txn.ID) == 0 { // TODO(tschottdorf): should really choose the first txn write here. firstKey := ba.Requests[0].GetInner().Header().Key newTxn := proto.NewTransaction(ba.Txn.Name, keys.KeyAddress(firstKey), ba.GetUserPriority(), ba.Txn.Isolation, tc.clock.Now(), tc.clock.MaxOffset().Nanoseconds()) // Use existing priority as a minimum. This is used on transaction // aborts to ratchet priority when creating successor transaction. if newTxn.Priority < ba.Txn.Priority { newTxn.Priority = ba.Txn.Priority } ba.Txn = newTxn } }
// getCachedRangeDescriptorLocked is a helper function to retrieve the // descriptor of the range which contains the given key, if present in the // cache. It is assumed that the caller holds a read lock on rdc.rangeCacheMu. func (rdc *rangeDescriptorCache) getCachedRangeDescriptorLocked(key proto.Key) ( rangeCacheKey, *proto.RangeDescriptor) { // The cache is indexed using the end-key of the range, but the // end-key is non-inclusive. If inclusive is false, we access the // cache using key.Next(). metaKey := keys.RangeMetaKey(key.Next()) k, v, ok := rdc.rangeCache.Ceil(rangeCacheKey(metaKey)) if !ok { return nil, nil } metaEndKey := k.(rangeCacheKey) rd := v.(*proto.RangeDescriptor) // Check that key actually belongs to range if !rd.ContainsKey(keys.KeyAddress(key)) { return nil, nil } return metaEndKey, rd }
func TestKeyAddress(t *testing.T) { defer leaktest.AfterTest(t) testCases := []struct { key proto.Key }{ {MakeNameMetadataKey(0, "BAR")}, {MakeNameMetadataKey(1, "BAR")}, {MakeNameMetadataKey(1, "foo")}, {MakeNameMetadataKey(2, "foo")}, {MakeDescMetadataKey(123)}, {MakeDescMetadataKey(124)}, } var lastKey proto.Key for i, test := range testCases { result := keys.KeyAddress(test.key) if result.Compare(lastKey) <= 0 { t.Errorf("%d: key address %q is <= %q", i, result, lastKey) } lastKey = result } }
// getCachedRangeDescriptorLocked is a helper function to retrieve the // descriptor of the range which contains the given key, if present in the // cache. It is assumed that the caller holds a read lock on rdc.rangeCacheMu. func (rdc *rangeDescriptorCache) getCachedRangeDescriptorLocked(key proto.Key, isReverse bool) ( rangeCacheKey, *proto.RangeDescriptor) { // The cache is indexed using the end-key of the range, but the // end-key is non-inclusive. var metaKey proto.Key if !isReverse { // If it is not reverse scan, we access the cache using key.Next(). metaKey = keys.RangeMetaKey(key.Next()) } else { // Because reverse scan request is begining at end key(exclusive),so we // access the cache using key directly. metaKey = keys.RangeMetaKey(key) } k, v, ok := rdc.rangeCache.Ceil(rangeCacheKey(metaKey)) if !ok { return nil, nil } metaEndKey := k.(rangeCacheKey) rd := v.(*proto.RangeDescriptor) // Check that key actually belongs to the range. if !rd.ContainsKey(keys.KeyAddress(key)) { // The key is the EndKey of the range in reverse scan, just return the range descriptor. if isReverse && key.Equal(rd.EndKey) { return metaEndKey, rd } return nil, nil } // The key is the StartKey of the range in reverse scan. We need to return the previous range // descriptor, but it is not in the cache yet. if isReverse && key.Equal(rd.StartKey) { return nil, nil } return metaEndKey, rd }
// ContainsKeyRange returns whether this range contains the specified // key range from start to end. func (r *Range) ContainsKeyRange(start, end proto.Key) bool { return r.Desc().ContainsKeyRange(keys.KeyAddress(start), keys.KeyAddress(end)) }
// ContainsKey returns whether this range contains the specified key. func (r *Range) ContainsKey(key proto.Key) bool { return r.Desc().ContainsKey(keys.KeyAddress(key)) }
func containsKeyRange(desc proto.RangeDescriptor, start, end proto.Key) bool { return desc.ContainsKeyRange(keys.KeyAddress(start), keys.KeyAddress(end)) }
func containsKey(desc proto.RangeDescriptor, key proto.Key) bool { return desc.ContainsKey(keys.KeyAddress(key)) }
// truncate restricts all contained requests to the given key range. // Even on error, the returned closure must be executed; it undoes any // truncations performed. // First, the boundaries of the truncation are obtained: This is the // intersection between [from,to) and the descriptor's range. // Secondly, all requests contained in the batch are "truncated" to // the resulting range, inserting NoopRequest appropriately to // replace requests which are left without a key range to operate on. // The number of non-noop requests after truncation is returned along // with a closure which must be executed to undo the truncation, even // in case of an error. // TODO(tschottdorf): Consider returning a new BatchRequest, which has more // overhead in the common case of a batch which never needs truncation but is // less magical. func truncate(br *proto.BatchRequest, desc *proto.RangeDescriptor, from, to proto.Key) (func(), int, error) { if !desc.ContainsKey(from) { from = desc.StartKey } if !desc.ContainsKeyRange(desc.StartKey, to) || to == nil { to = desc.EndKey } truncateOne := func(args proto.Request) (bool, []func(), error) { if _, ok := args.(*proto.NoopRequest); ok { return true, nil, nil } header := args.Header() if !proto.IsRange(args) { if len(header.EndKey) > 0 { return false, nil, util.Errorf("%T is not a range command, but EndKey is set", args) } if !desc.ContainsKey(keys.KeyAddress(header.Key)) { return true, nil, nil } return false, nil, nil } var undo []func() key, endKey := header.Key, header.EndKey keyAddr, endKeyAddr := keys.KeyAddress(key), keys.KeyAddress(endKey) if keyAddr.Less(from) { undo = append(undo, func() { header.Key = key }) header.Key = from keyAddr = from } if !endKeyAddr.Less(to) { undo = append(undo, func() { header.EndKey = endKey }) header.EndKey = to endKeyAddr = to } // Check whether the truncation has left any keys in the range. If not, // we need to cut it out of the request. return !keyAddr.Less(endKeyAddr), undo, nil } var fns []func() gUndo := func() { for _, f := range fns { f() } } var numNoop int for pos, arg := range br.Requests { omit, undo, err := truncateOne(arg.GetValue().(proto.Request)) if omit { numNoop++ nReq := &proto.RequestUnion{} nReq.SetValue(&proto.NoopRequest{}) oReq := br.Requests[pos] br.Requests[pos] = *nReq posCpy := pos // for closure undo = append(undo, func() { br.Requests[posCpy] = oReq }) } fns = append(fns, undo...) if err != nil { return gUndo, 0, err } } return gUndo, len(br.Requests) - numNoop, nil }