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  }