github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_lease_request.go (about) 1 // Copyright 2014 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 batcheval 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" 17 "github.com/cockroachdb/cockroach/pkg/roachpb" 18 "github.com/cockroachdb/cockroach/pkg/storage" 19 ) 20 21 func init() { 22 RegisterReadWriteCommand(roachpb.RequestLease, declareKeysRequestLease, RequestLease) 23 } 24 25 // RequestLease sets the range lease for this range. The command fails 26 // only if the desired start timestamp collides with a previous lease. 27 // Otherwise, the start timestamp is wound back to right after the expiration 28 // of the previous lease (or zero). If this range replica is already the lease 29 // holder, the expiration will be extended or shortened as indicated. For a new 30 // lease, all duties required of the range lease holder are commenced, including 31 // releasing all latches and clearing the timestamp cache. 32 func RequestLease( 33 ctx context.Context, readWriter storage.ReadWriter, cArgs CommandArgs, resp roachpb.Response, 34 ) (result.Result, error) { 35 // When returning an error from this method, must always return a 36 // newFailedLeaseTrigger() to satisfy stats. 37 args := cArgs.Args.(*roachpb.RequestLeaseRequest) 38 39 prevLease, _ := cArgs.EvalCtx.GetLease() 40 rErr := &roachpb.LeaseRejectedError{ 41 Existing: prevLease, 42 Requested: args.Lease, 43 } 44 45 // For now, don't allow replicas of type LEARNER to be leaseholders. There's 46 // no reason this wouldn't work in principle, but it seems inadvisable. In 47 // particular, learners can't become raft leaders, so we wouldn't be able to 48 // co-locate the leaseholder + raft leader, which is going to affect tail 49 // latencies. Additionally, as of the time of writing, learner replicas are 50 // only used for a short time in replica addition, so it's not worth working 51 // out the edge cases. If we decide to start using long-lived learners at some 52 // point, that math may change. 53 // 54 // If this check is removed at some point, the filtering of learners on the 55 // sending side would have to be removed as well. 56 if err := checkCanReceiveLease(&args.Lease, cArgs.EvalCtx); err != nil { 57 rErr.Message = err.Error() 58 return newFailedLeaseTrigger(false /* isTransfer */), rErr 59 } 60 61 // MIGRATION(tschottdorf): needed to apply Raft commands which got proposed 62 // before the StartStasis field was introduced. 63 newLease := args.Lease 64 if newLease.DeprecatedStartStasis == nil { 65 newLease.DeprecatedStartStasis = newLease.Expiration 66 } 67 68 isExtension := prevLease.Replica.StoreID == newLease.Replica.StoreID 69 effectiveStart := newLease.Start 70 71 // Wind the start timestamp back as far towards the previous lease as we 72 // can. That'll make sure that when multiple leases are requested out of 73 // order at the same replica (after all, they use the request timestamp, 74 // which isn't straight out of our local clock), they all succeed unless 75 // they have a "real" issue with a previous lease. Example: Assuming no 76 // previous lease, one request for [5, 15) followed by one for [0, 15) 77 // would fail without this optimization. With it, the first request 78 // effectively gets the lease for [0, 15), which the second one can commit 79 // again (even extending your own lease is possible; see below). 80 // 81 // If this is our lease (or no prior lease exists), we effectively absorb 82 // the old lease. This allows multiple requests from the same replica to 83 // merge without ticking away from the minimal common start timestamp. It 84 // also has the positive side-effect of fixing #3561, which was caused by 85 // the absence of replay protection. 86 if prevLease.Replica.StoreID == 0 || isExtension { 87 effectiveStart.Backward(prevLease.Start) 88 // If the lease holder promised to not propose any commands below 89 // MinProposedTS, it must also not be allowed to extend a lease before that 90 // timestamp. We make sure that when a node restarts, its earlier in-flight 91 // commands (which are not tracked by the spanlatch manager post restart) 92 // receive an error under the new lease by making sure the sequence number 93 // of that lease is higher. This in turn is achieved by forwarding its start 94 // time here, which makes it not Equivalent() to the preceding lease for the 95 // same store. 96 // 97 // Note also that leasePostApply makes sure to update the timestamp cache in 98 // this case: even though the lease holder does not change, the the sequence 99 // number does and this triggers a low water mark bump. 100 // 101 // The bug prevented with this is unlikely to occur in practice 102 // since earlier commands usually apply before this lease will. 103 if ts := args.MinProposedTS; isExtension && ts != nil { 104 effectiveStart.Forward(*ts) 105 } 106 107 } else if prevLease.Type() == roachpb.LeaseExpiration { 108 effectiveStart.Backward(prevLease.Expiration.Next()) 109 } 110 111 if isExtension { 112 if effectiveStart.Less(prevLease.Start) { 113 rErr.Message = "extension moved start timestamp backwards" 114 return newFailedLeaseTrigger(false /* isTransfer */), rErr 115 } 116 if newLease.Type() == roachpb.LeaseExpiration { 117 // NB: Avoid mutating pointers in the argument which might be shared with 118 // the caller. 119 t := *newLease.Expiration 120 newLease.Expiration = &t 121 newLease.Expiration.Forward(prevLease.GetExpiration()) 122 } 123 } else if prevLease.Type() == roachpb.LeaseExpiration && effectiveStart.Less(prevLease.GetExpiration()) { 124 rErr.Message = "requested lease overlaps previous lease" 125 return newFailedLeaseTrigger(false /* isTransfer */), rErr 126 } 127 newLease.Start = effectiveStart 128 return evalNewLease(ctx, cArgs.EvalCtx, readWriter, cArgs.Stats, 129 newLease, prevLease, isExtension, false /* isTransfer */) 130 }