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  }