github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_read.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" 17 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" 18 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency" 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" 21 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" 22 "github.com/cockroachdb/cockroach/pkg/roachpb" 23 "github.com/cockroachdb/cockroach/pkg/storage" 24 "github.com/cockroachdb/cockroach/pkg/util" 25 "github.com/cockroachdb/cockroach/pkg/util/log" 26 "github.com/kr/pretty" 27 ) 28 29 // executeReadOnlyBatch is the execution logic for client requests which do not 30 // mutate the range's replicated state. The method uses a single RocksDB 31 // iterator to evaluate the batch and then updates the timestamp cache to 32 // reflect the key spans that it read. 33 func (r *Replica) executeReadOnlyBatch( 34 ctx context.Context, ba *roachpb.BatchRequest, st kvserverpb.LeaseStatus, g *concurrency.Guard, 35 ) (br *roachpb.BatchResponse, _ *concurrency.Guard, pErr *roachpb.Error) { 36 r.readOnlyCmdMu.RLock() 37 defer r.readOnlyCmdMu.RUnlock() 38 39 // Verify that the batch can be executed. 40 if err := r.checkExecutionCanProceed(ba, g, &st); err != nil { 41 return nil, g, roachpb.NewError(err) 42 } 43 44 // Evaluate read-only batch command. 45 spans := g.LatchSpans() 46 rec := NewReplicaEvalContext(r, spans) 47 48 // TODO(irfansharif): It's unfortunate that in this read-only code path, 49 // we're stuck with a ReadWriter because of the way evaluateBatch is 50 // designed. 51 rw := r.store.Engine().NewReadOnly() 52 if util.RaceEnabled { 53 rw = spanset.NewReadWriterAt(rw, spans, ba.Timestamp) 54 } 55 defer rw.Close() 56 57 // TODO(nvanbenschoten): once all replicated intents are pulled into the 58 // concurrency manager's lock-table, we can be sure that if we reached this 59 // point, we will not conflict with any of them during evaluation. This in 60 // turn means that we can bump the timestamp cache *before* evaluation 61 // without risk of starving writes. Once we start doing that, we're free to 62 // release latches immediately after we acquire an engine iterator as long 63 // as we're performing a non-locking read. 64 65 var result result.Result 66 br, result, pErr = r.executeReadOnlyBatchWithServersideRefreshes(ctx, rw, rec, ba, spans) 67 68 // If the request hit a server-side concurrency retry error, immediately 69 // proagate the error. Don't assume ownership of the concurrency guard. 70 if isConcurrencyRetryError(pErr) { 71 return nil, g, pErr 72 } 73 74 // Handle any local (leaseholder-only) side-effects of the request. 75 intents := result.Local.DetachEncounteredIntents() 76 if pErr == nil { 77 pErr = r.handleReadOnlyLocalEvalResult(ctx, ba, result.Local) 78 } 79 80 // Otherwise, update the timestamp cache and release the concurrency guard. 81 ec, g := endCmds{repl: r, g: g}, nil 82 ec.done(ctx, ba, br, pErr) 83 84 // Semi-synchronously process any intents that need resolving here in 85 // order to apply back pressure on the client which generated them. The 86 // resolution is semi-synchronous in that there is a limited number of 87 // outstanding asynchronous resolution tasks allowed after which 88 // further calls will block. 89 if len(intents) > 0 { 90 log.Eventf(ctx, "submitting %d intents to asynchronous processing", len(intents)) 91 // We only allow synchronous intent resolution for consistent requests. 92 // Intent resolution is async/best-effort for inconsistent requests. 93 // 94 // An important case where this logic is necessary is for RangeLookup 95 // requests. In their case, synchronous intent resolution can deadlock 96 // if the request originated from the local node which means the local 97 // range descriptor cache has an in-flight RangeLookup request which 98 // prohibits any concurrent requests for the same range. See #17760. 99 allowSyncProcessing := ba.ReadConsistency == roachpb.CONSISTENT 100 if err := r.store.intentResolver.CleanupIntentsAsync(ctx, intents, allowSyncProcessing); err != nil { 101 log.Warningf(ctx, "%v", err) 102 } 103 } 104 105 if pErr != nil { 106 log.VErrEventf(ctx, 3, "%v", pErr.String()) 107 } else { 108 log.Event(ctx, "read completed") 109 } 110 return br, nil, pErr 111 } 112 113 // executeReadOnlyBatchWithServersideRefreshes invokes evaluateBatch and retries 114 // at a higher timestamp in the event of some retriable errors if allowed by the 115 // batch/txn. 116 func (r *Replica) executeReadOnlyBatchWithServersideRefreshes( 117 ctx context.Context, 118 rw storage.ReadWriter, 119 rec batcheval.EvalContext, 120 ba *roachpb.BatchRequest, 121 latchSpans *spanset.SpanSet, 122 ) (br *roachpb.BatchResponse, res result.Result, pErr *roachpb.Error) { 123 log.Event(ctx, "executing read-only batch") 124 125 for retries := 0; ; retries++ { 126 if retries > 0 { 127 log.VEventf(ctx, 2, "server-side retry of batch") 128 } 129 br, res, pErr = evaluateBatch(ctx, kvserverbase.CmdIDKey(""), rw, rec, nil, ba, true /* readOnly */) 130 131 // If we can retry, set a higher batch timestamp and continue. 132 // Allow one retry only. 133 if pErr == nil || retries > 0 || !canDoServersideRetry(ctx, pErr, ba, br, latchSpans, nil /* deadline */) { 134 break 135 } 136 } 137 138 if pErr != nil { 139 // Failed read-only batches can't have any Result except for what's 140 // whitelisted here. 141 res.Local = result.LocalResult{ 142 EncounteredIntents: res.Local.DetachEncounteredIntents(), 143 Metrics: res.Local.Metrics, 144 } 145 return nil, res, pErr 146 } 147 return br, res, nil 148 } 149 150 func (r *Replica) handleReadOnlyLocalEvalResult( 151 ctx context.Context, ba *roachpb.BatchRequest, lResult result.LocalResult, 152 ) *roachpb.Error { 153 // Fields for which no action is taken in this method are zeroed so that 154 // they don't trigger an assertion at the end of the method (which checks 155 // that all fields were handled). 156 { 157 lResult.Reply = nil 158 } 159 160 if lResult.AcquiredLocks != nil { 161 // These will all be unreplicated locks. 162 for i := range lResult.AcquiredLocks { 163 r.concMgr.OnLockAcquired(ctx, &lResult.AcquiredLocks[i]) 164 } 165 lResult.AcquiredLocks = nil 166 } 167 168 if lResult.MaybeWatchForMerge { 169 // A merge is (likely) about to be carried out, and this replica needs 170 // to block all traffic until the merge either commits or aborts. See 171 // docs/tech-notes/range-merges.md. 172 if err := r.maybeWatchForMerge(ctx); err != nil { 173 return roachpb.NewError(err) 174 } 175 lResult.MaybeWatchForMerge = false 176 } 177 178 if !lResult.IsZero() { 179 log.Fatalf(ctx, "unhandled field in LocalEvalResult: %s", pretty.Diff(lResult, result.LocalResult{})) 180 } 181 return nil 182 }