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 }