github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_application_decoder.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/apply" 17 "github.com/cockroachdb/cockroach/pkg/util/log" 18 "github.com/cockroachdb/cockroach/pkg/util/quotapool" 19 "github.com/cockroachdb/cockroach/pkg/util/tracing" 20 "github.com/opentracing/opentracing-go" 21 "go.etcd.io/etcd/raft/raftpb" 22 ) 23 24 // replica_application_*.go files provide concrete implementations of 25 // the interfaces defined in the storage/apply package: 26 // 27 // replica_application_state_machine.go -> apply.StateMachine 28 // replica_application_decoder.go -> apply.Decoder 29 // replica_application_cmd.go -> apply.Command (and variants) 30 // replica_application_cmd_buf.go -> apply.CommandIterator (and variants) 31 // replica_application_cmd_buf.go -> apply.CommandList (and variants) 32 // 33 // These allow Replica to interface with the storage/apply package. 34 35 // replicaDecoder implements the apply.Decoder interface. 36 // 37 // The object is capable of decoding committed raft entries into a list of 38 // replicatedCmd objects (which implement all variants of apply.Command), binding 39 // these commands to their local proposals, and providing an iterator over these 40 // commands. 41 type replicaDecoder struct { 42 r *Replica 43 cmdBuf replicatedCmdBuf 44 } 45 46 // getDecoder returns the Replica's apply.Decoder. The Replica's raftMu 47 // is held for the entire lifetime of the replicaDecoder. 48 func (r *Replica) getDecoder() *replicaDecoder { 49 d := &r.raftMu.decoder 50 d.r = r 51 return d 52 } 53 54 // DecodeAndBind implements the apply.Decoder interface. 55 func (d *replicaDecoder) DecodeAndBind(ctx context.Context, ents []raftpb.Entry) (bool, error) { 56 if err := d.decode(ctx, ents); err != nil { 57 return false, err 58 } 59 anyLocal := d.retrieveLocalProposals(ctx) 60 d.createTracingSpans(ctx) 61 return anyLocal, nil 62 } 63 64 // decode decodes the provided entries into the decoder. 65 func (d *replicaDecoder) decode(ctx context.Context, ents []raftpb.Entry) error { 66 for i := range ents { 67 ent := &ents[i] 68 if err := d.cmdBuf.allocate().decode(ctx, ent); err != nil { 69 return err 70 } 71 } 72 return nil 73 } 74 75 // retrieveLocalProposals binds each of the decoder's commands to their local 76 // proposals if they were proposed locally. The method also sets the ctx fields 77 // on all commands. 78 func (d *replicaDecoder) retrieveLocalProposals(ctx context.Context) (anyLocal bool) { 79 d.r.mu.Lock() 80 defer d.r.mu.Unlock() 81 // Assign all the local proposals first then delete all of them from the map 82 // in a second pass. This ensures that we retrieve all proposals correctly 83 // even if the applier has multiple entries for the same proposal, in which 84 // case the proposal was reproposed (either under its original or a new 85 // MaxLeaseIndex) which we handle in a second pass below. 86 var it replicatedCmdBufSlice 87 for it.init(&d.cmdBuf); it.Valid(); it.Next() { 88 cmd := it.cur() 89 cmd.proposal = d.r.mu.proposals[cmd.idKey] 90 anyLocal = anyLocal || cmd.IsLocal() 91 } 92 if !anyLocal && d.r.mu.proposalQuota == nil { 93 // Fast-path. 94 return false 95 } 96 for it.init(&d.cmdBuf); it.Valid(); it.Next() { 97 cmd := it.cur() 98 var toRelease *quotapool.IntAlloc 99 shouldRemove := cmd.IsLocal() && 100 // If this entry does not have the most up-to-date view of the 101 // corresponding proposal's maximum lease index then the proposal 102 // must have been reproposed with a higher lease index. (see 103 // tryReproposeWithNewLeaseIndex). In that case, there's a newer 104 // version of the proposal in the pipeline, so don't remove the 105 // proposal from the map. We expect this entry to be rejected by 106 // checkForcedErr. 107 cmd.raftCmd.MaxLeaseIndex == cmd.proposal.command.MaxLeaseIndex 108 if shouldRemove { 109 // Delete the proposal from the proposals map. There may be reproposals 110 // of the proposal in the pipeline, but those will all have the same max 111 // lease index, meaning that they will all be rejected after this entry 112 // applies (successfully or otherwise). If tryReproposeWithNewLeaseIndex 113 // picks up the proposal on failure, it will re-add the proposal to the 114 // proposal map, but this won't affect this replicaApplier. 115 // 116 // While here, add the proposal's quota size to the quota release queue. 117 // We check the proposal map again first to avoid double free-ing quota 118 // when reproposals from the same proposal end up in the same entry 119 // application batch. 120 delete(d.r.mu.proposals, cmd.idKey) 121 toRelease = cmd.proposal.quotaAlloc 122 cmd.proposal.quotaAlloc = nil 123 } 124 // At this point we're not guaranteed to have proposalQuota initialized, 125 // the same is true for quotaReleaseQueues. Only queue the proposal's 126 // quota for release if the proposalQuota is initialized. 127 if d.r.mu.proposalQuota != nil { 128 d.r.mu.quotaReleaseQueue = append(d.r.mu.quotaReleaseQueue, toRelease) 129 } 130 } 131 return anyLocal 132 } 133 134 // createTracingSpans creates and assigns a new tracing span for each decoded 135 // command. If a command was proposed locally, it will be given a tracing span 136 // that follows from its proposal's span. 137 func (d *replicaDecoder) createTracingSpans(ctx context.Context) { 138 const opName = "raft application" 139 var it replicatedCmdBufSlice 140 for it.init(&d.cmdBuf); it.Valid(); it.Next() { 141 cmd := it.cur() 142 if cmd.IsLocal() { 143 cmd.ctx, cmd.sp = tracing.ForkCtxSpan(cmd.proposal.ctx, opName) 144 } else if cmd.raftCmd.TraceData != nil { 145 // The proposal isn't local, and trace data is available. Extract 146 // the span context and start a server-side span. 147 spanCtx, err := d.r.AmbientContext.Tracer.Extract( 148 opentracing.TextMap, opentracing.TextMapCarrier(cmd.raftCmd.TraceData)) 149 if err != nil { 150 log.Errorf(ctx, "unable to extract trace data from raft command: %s", err) 151 } else { 152 cmd.sp = d.r.AmbientContext.Tracer.StartSpan( 153 "raft application", opentracing.FollowsFrom(spanCtx)) 154 cmd.ctx = opentracing.ContextWithSpan(ctx, cmd.sp) 155 } 156 } else { 157 cmd.ctx, cmd.sp = tracing.ForkCtxSpan(ctx, opName) 158 } 159 } 160 } 161 162 // NewCommandIter implements the apply.Decoder interface. 163 func (d *replicaDecoder) NewCommandIter() apply.CommandIterator { 164 it := d.cmdBuf.newIter() 165 it.init(&d.cmdBuf) 166 return it 167 } 168 169 // Reset implements the apply.Decoder interface. 170 func (d *replicaDecoder) Reset() { 171 d.cmdBuf.clear() 172 }