github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_application_cmd.go (about)

     1  // Copyright 2019 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 kvserver
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result"
    17  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase"
    18  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/util/log"
    21  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    22  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    23  	"github.com/cockroachdb/errors"
    24  	opentracing "github.com/opentracing/opentracing-go"
    25  	"go.etcd.io/etcd/raft/raftpb"
    26  )
    27  
    28  // replica_application_*.go files provide concrete implementations of
    29  // the interfaces defined in the storage/apply package:
    30  //
    31  // replica_application_state_machine.go  ->  apply.StateMachine
    32  // replica_application_decoder.go        ->  apply.Decoder
    33  // replica_application_cmd.go            ->  apply.Command         (and variants)
    34  // replica_application_cmd_buf.go        ->  apply.CommandIterator (and variants)
    35  // replica_application_cmd_buf.go        ->  apply.CommandList     (and variants)
    36  //
    37  // These allow Replica to interface with the storage/apply package.
    38  
    39  // replicatedCmd stores the state required to apply a single raft entry to a
    40  // replica. The state is accumulated in stages which occur in apply.Task. From
    41  // a high level, the command is decoded from a committed raft entry, then if it
    42  // was proposed locally the proposal is populated from the replica's proposals
    43  // map, then the command is staged into a replicaAppBatch by writing its update
    44  // to the batch's engine.Batch and applying its "trivial" side-effects to the
    45  // batch's view of ReplicaState. Then the batch is committed, the side-effects
    46  // are applied and the local result is processed.
    47  type replicatedCmd struct {
    48  	ent              *raftpb.Entry // the raft.Entry being applied
    49  	decodedRaftEntry               // decoded from ent
    50  
    51  	// proposal is populated on the proposing Replica only and comes from the
    52  	// Replica's proposal map.
    53  	proposal *ProposalData
    54  
    55  	// ctx is a context that follows from the proposal's context if it was
    56  	// proposed locally. Otherwise, it will follow from the context passed to
    57  	// ApplyCommittedEntries.
    58  	ctx context.Context
    59  	// sp is the tracing span corresponding to ctx. It is closed in
    60  	// FinishAndAckOutcome. This span "follows from" the proposer's span (even
    61  	// when the proposer is remote; we marshall tracing info through the
    62  	// proposal).
    63  	sp opentracing.Span
    64  
    65  	// The following fields are set in shouldApplyCommand when we validate that
    66  	// a command applies given the current lease and GC threshold. The process
    67  	// of setting these fields is what transforms an apply.Command into an
    68  	// apply.CheckedCommand.
    69  	leaseIndex    uint64
    70  	forcedErr     *roachpb.Error
    71  	proposalRetry proposalReevaluationReason
    72  	// splitMergeUnlock is acquired for splits and merges when they are staged
    73  	// in the application batch and called after the command's side effects
    74  	// are applied.
    75  	splitMergeUnlock func()
    76  
    77  	// The following fields are set after the data has been written to the
    78  	// storage engine in prepareLocalResult. The process of setting these fields
    79  	// is what transforms an apply.CheckedCommand into an apply.AppliedCommand.
    80  	localResult *result.LocalResult
    81  	response    proposalResult
    82  }
    83  
    84  // decodedRaftEntry represents the deserialized content of a raftpb.Entry.
    85  type decodedRaftEntry struct {
    86  	idKey      kvserverbase.CmdIDKey
    87  	raftCmd    kvserverpb.RaftCommand
    88  	confChange *decodedConfChange // only non-nil for config changes
    89  }
    90  
    91  // decodedConfChange represents the fields of a config change raft command.
    92  type decodedConfChange struct {
    93  	raftpb.ConfChangeI
    94  	ConfChangeContext
    95  }
    96  
    97  // decode decodes the entry e into the replicatedCmd.
    98  func (c *replicatedCmd) decode(ctx context.Context, e *raftpb.Entry) error {
    99  	c.ent = e
   100  	return c.decodedRaftEntry.decode(ctx, e)
   101  }
   102  
   103  // Index implements the apply.Command interface.
   104  func (c *replicatedCmd) Index() uint64 {
   105  	return c.ent.Index
   106  }
   107  
   108  // IsTrivial implements the apply.Command interface.
   109  func (c *replicatedCmd) IsTrivial() bool {
   110  	return isTrivial(c.replicatedResult())
   111  }
   112  
   113  // IsLocal implements the apply.Command interface.
   114  func (c *replicatedCmd) IsLocal() bool {
   115  	return c.proposal != nil
   116  }
   117  
   118  // Rejected implements the apply.CheckedCommand interface.
   119  func (c *replicatedCmd) Rejected() bool {
   120  	return c.forcedErr != nil
   121  }
   122  
   123  // CanAckBeforeApplication implements the apply.CheckedCommand interface.
   124  func (c *replicatedCmd) CanAckBeforeApplication() bool {
   125  	// CanAckBeforeApplication determines whether the request type is compatible
   126  	// with acknowledgement of success before it has been applied. For now, this
   127  	// determination is based on whether the request is performing transactional
   128  	// writes. This could be exposed as an option on the batch header instead.
   129  	//
   130  	// We don't try to ack async consensus writes before application because we
   131  	// know that there isn't a client waiting for the result.
   132  	req := c.proposal.Request
   133  	return req.IsIntentWrite() && !req.AsyncConsensus
   134  }
   135  
   136  // AckSuccess implements the apply.CheckedCommand interface.
   137  func (c *replicatedCmd) AckSuccess() error {
   138  	if !c.IsLocal() {
   139  		return nil
   140  	}
   141  
   142  	// Signal the proposal's response channel with the result.
   143  	// Make a copy of the response to avoid data races between client mutations
   144  	// of the response and use of the response in endCmds.done when the command
   145  	// is finished.
   146  	var resp proposalResult
   147  	reply := *c.proposal.Local.Reply
   148  	reply.Responses = append([]roachpb.ResponseUnion(nil), reply.Responses...)
   149  	resp.Reply = &reply
   150  	resp.EncounteredIntents = c.proposal.Local.DetachEncounteredIntents()
   151  	resp.EndTxns = c.proposal.Local.DetachEndTxns(false /* alwaysOnly */)
   152  	c.proposal.signalProposalResult(resp)
   153  	return nil
   154  }
   155  
   156  // FinishAndAckOutcome implements the apply.AppliedCommand interface.
   157  func (c *replicatedCmd) FinishAndAckOutcome(ctx context.Context) error {
   158  	tracing.FinishSpan(c.sp)
   159  	if c.IsLocal() {
   160  		c.proposal.finishApplication(ctx, c.response)
   161  	}
   162  	return nil
   163  }
   164  
   165  // decode decodes the entry e into the decodedRaftEntry.
   166  func (d *decodedRaftEntry) decode(ctx context.Context, e *raftpb.Entry) error {
   167  	*d = decodedRaftEntry{}
   168  	// etcd raft sometimes inserts nil commands, ours are never nil.
   169  	// This case is handled upstream of this call.
   170  	if len(e.Data) == 0 {
   171  		return nil
   172  	}
   173  	switch e.Type {
   174  	case raftpb.EntryNormal:
   175  		return d.decodeNormalEntry(e)
   176  	case raftpb.EntryConfChange, raftpb.EntryConfChangeV2:
   177  		return d.decodeConfChangeEntry(e)
   178  	default:
   179  		log.Fatalf(ctx, "unexpected Raft entry: %v", e)
   180  		return nil // unreachable
   181  	}
   182  }
   183  
   184  func (d *decodedRaftEntry) decodeNormalEntry(e *raftpb.Entry) error {
   185  	var encodedCommand []byte
   186  	d.idKey, encodedCommand = DecodeRaftCommand(e.Data)
   187  	// An empty command is used to unquiesce a range and wake the
   188  	// leader. Clear commandID so it's ignored for processing.
   189  	if len(encodedCommand) == 0 {
   190  		d.idKey = ""
   191  	} else if err := protoutil.Unmarshal(encodedCommand, &d.raftCmd); err != nil {
   192  		return wrapWithNonDeterministicFailure(err, "while unmarshaling entry")
   193  	}
   194  	return nil
   195  }
   196  
   197  func (d *decodedRaftEntry) decodeConfChangeEntry(e *raftpb.Entry) error {
   198  	d.confChange = &decodedConfChange{}
   199  
   200  	switch e.Type {
   201  	case raftpb.EntryConfChange:
   202  		var cc raftpb.ConfChange
   203  		if err := protoutil.Unmarshal(e.Data, &cc); err != nil {
   204  			return wrapWithNonDeterministicFailure(err, "while unmarshaling ConfChange")
   205  		}
   206  		d.confChange.ConfChangeI = cc
   207  	case raftpb.EntryConfChangeV2:
   208  		var cc raftpb.ConfChangeV2
   209  		if err := protoutil.Unmarshal(e.Data, &cc); err != nil {
   210  			return wrapWithNonDeterministicFailure(err, "while unmarshaling ConfChangeV2")
   211  		}
   212  		d.confChange.ConfChangeI = cc
   213  	default:
   214  		const msg = "unknown entry type"
   215  		err := errors.New(msg)
   216  		return wrapWithNonDeterministicFailure(err, msg)
   217  	}
   218  	if err := protoutil.Unmarshal(d.confChange.AsV2().Context, &d.confChange.ConfChangeContext); err != nil {
   219  		return wrapWithNonDeterministicFailure(err, "while unmarshaling ConfChangeContext")
   220  	}
   221  	if err := protoutil.Unmarshal(d.confChange.Payload, &d.raftCmd); err != nil {
   222  		return wrapWithNonDeterministicFailure(err, "while unmarshaling RaftCommand")
   223  	}
   224  	d.idKey = kvserverbase.CmdIDKey(d.confChange.CommandID)
   225  	return nil
   226  }
   227  
   228  func (d *decodedRaftEntry) replicatedResult() *kvserverpb.ReplicatedEvalResult {
   229  	return &d.raftCmd.ReplicatedEvalResult
   230  }