github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_lease.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 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/keys" 18 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/storage" 23 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 24 "github.com/cockroachdb/errors" 25 ) 26 27 func declareKeysRequestLease( 28 desc *roachpb.RangeDescriptor, 29 header roachpb.Header, 30 req roachpb.Request, 31 latchSpans, _ *spanset.SpanSet, 32 ) { 33 latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: keys.RangeLeaseKey(header.RangeID)}) 34 latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: keys.RangeDescriptorKey(desc.StartKey)}) 35 } 36 37 func newFailedLeaseTrigger(isTransfer bool) result.Result { 38 var trigger result.Result 39 trigger.Local.Metrics = new(result.Metrics) 40 if isTransfer { 41 trigger.Local.Metrics.LeaseTransferError = 1 42 } else { 43 trigger.Local.Metrics.LeaseRequestError = 1 44 } 45 return trigger 46 } 47 48 func checkCanReceiveLease(newLease *roachpb.Lease, rec EvalContext) error { 49 repDesc, ok := rec.Desc().GetReplicaDescriptorByID(newLease.Replica.ReplicaID) 50 if !ok { 51 if newLease.Replica.StoreID == rec.StoreID() { 52 return errors.AssertionFailedf( 53 `could not find replica for store %s in %s`, rec.StoreID(), rec.Desc()) 54 } 55 return errors.Errorf(`replica %s not found in %s`, newLease.Replica, rec.Desc()) 56 } else if t := repDesc.GetType(); t != roachpb.VOTER_FULL { 57 // NB: there's no harm in transferring the lease to a VOTER_INCOMING, 58 // but we disallow it anyway. On the other hand, transferring to 59 // VOTER_OUTGOING would be a pretty bad idea since those voters are 60 // dropped when transitioning out of the joint config, which then 61 // amounts to removing the leaseholder without any safety precautions. 62 // This would either wedge the range or allow illegal reads to be 63 // served. 64 // 65 // Since the leaseholder can't remove itself and is a VOTER_FULL, we 66 // also know that in any configuration there's at least one VOTER_FULL. 67 // 68 // TODO(tbg): if this code path is hit during a lease transfer (we check 69 // upstream of raft, but this check has false negatives) then we are in 70 // a situation where the leaseholder is a node that has set its 71 // minProposedTS and won't be using its lease any more. Either the setting 72 // of minProposedTS needs to be "reversible" (tricky) or we make the 73 // lease evaluation succeed, though with a lease that's "invalid" so that 74 // a new lease can be requested right after. 75 return errors.Errorf(`replica %s of type %s cannot hold lease`, repDesc, t) 76 } 77 return nil 78 } 79 80 // evalNewLease checks that the lease contains a valid interval and that 81 // the new lease holder is still a member of the replica set, and then proceeds 82 // to write the new lease to the batch, emitting an appropriate trigger. 83 // 84 // The new lease might be a lease for a range that didn't previously have an 85 // active lease, might be an extension or a lease transfer. 86 // 87 // isExtension should be set if the lease holder does not change with this 88 // lease. If it doesn't change, we don't need the application of this lease to 89 // block reads. 90 // 91 // TODO(tschottdorf): refactoring what's returned from the trigger here makes 92 // sense to minimize the amount of code intolerant of rolling updates. 93 func evalNewLease( 94 ctx context.Context, 95 rec EvalContext, 96 readWriter storage.ReadWriter, 97 ms *enginepb.MVCCStats, 98 lease roachpb.Lease, 99 prevLease roachpb.Lease, 100 isExtension bool, 101 isTransfer bool, 102 ) (result.Result, error) { 103 // When returning an error from this method, must always return 104 // a newFailedLeaseTrigger() to satisfy stats. 105 106 // Ensure either an Epoch is set or Start < Expiration. 107 if (lease.Type() == roachpb.LeaseExpiration && lease.GetExpiration().LessEq(lease.Start)) || 108 (lease.Type() == roachpb.LeaseEpoch && lease.Expiration != nil) { 109 // This amounts to a bug. 110 return newFailedLeaseTrigger(isTransfer), 111 &roachpb.LeaseRejectedError{ 112 Existing: prevLease, 113 Requested: lease, 114 Message: fmt.Sprintf("illegal lease: epoch=%d, interval=[%s, %s)", 115 lease.Epoch, lease.Start, lease.Expiration), 116 } 117 } 118 119 // Verify that requesting replica is part of the current replica set. 120 desc := rec.Desc() 121 if _, ok := desc.GetReplicaDescriptor(lease.Replica.StoreID); !ok { 122 return newFailedLeaseTrigger(isTransfer), 123 &roachpb.LeaseRejectedError{ 124 Existing: prevLease, 125 Requested: lease, 126 Message: "replica not found", 127 } 128 } 129 130 // Requests should not set the sequence number themselves. Set the sequence 131 // number here based on whether the lease is equivalent to the one it's 132 // succeeding. 133 if lease.Sequence != 0 { 134 return newFailedLeaseTrigger(isTransfer), 135 &roachpb.LeaseRejectedError{ 136 Existing: prevLease, 137 Requested: lease, 138 Message: "sequence number should not be set", 139 } 140 } 141 if prevLease.Equivalent(lease) { 142 // If the proposed lease is equivalent to the previous lease, it is 143 // given the same sequence number. This is subtle, but is important 144 // to ensure that leases which are meant to be considered the same 145 // lease for the purpose of matching leases during command execution 146 // (see Lease.Equivalent) will be considered so. For example, an 147 // extension to an expiration-based lease will result in a new lease 148 // with the same sequence number. 149 lease.Sequence = prevLease.Sequence 150 } else { 151 // We set the new lease sequence to one more than the previous lease 152 // sequence. This is safe and will never result in repeated lease 153 // sequences because the sequence check beneath Raft acts as an atomic 154 // compare-and-swap of sorts. If two lease requests are proposed in 155 // parallel, both with the same previous lease, only one will be 156 // accepted and the other will get a LeaseRejectedError and need to 157 // retry with a different sequence number. This is actually exactly what 158 // the sequence number is used to enforce! 159 lease.Sequence = prevLease.Sequence + 1 160 } 161 162 // Store the lease to disk & in-memory. 163 if err := MakeStateLoader(rec).SetLease(ctx, readWriter, ms, lease); err != nil { 164 return newFailedLeaseTrigger(isTransfer), err 165 } 166 167 var pd result.Result 168 pd.Replicated.State = &kvserverpb.ReplicaState{ 169 Lease: &lease, 170 } 171 pd.Replicated.PrevLeaseProposal = prevLease.ProposedTS 172 173 pd.Local.Metrics = new(result.Metrics) 174 if isTransfer { 175 pd.Local.Metrics.LeaseTransferSuccess = 1 176 } else { 177 pd.Local.Metrics.LeaseRequestSuccess = 1 178 } 179 return pd, nil 180 }