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

     1  // Copyright 2014 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 batcheval
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result"
    17  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
    18  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    19  	"github.com/cockroachdb/cockroach/pkg/storage"
    20  	"github.com/cockroachdb/cockroach/pkg/util/log"
    21  	"github.com/cockroachdb/errors"
    22  )
    23  
    24  // CollectIntentRows collects the provisional key-value pairs for each intent
    25  // provided.
    26  //
    27  // The method accepts a reader and flag indicating whether a prefix iterator
    28  // should be used when creating an iterator from the reader. This flexibility
    29  // works around a limitation of the Engine.NewReadOnly interface where prefix
    30  // iterators and non-prefix iterators pulled from the same read-only engine are
    31  // not guaranteed to provide a consistent snapshot of the underlying engine.
    32  // This function expects to be able to retrieve the corresponding provisional
    33  // value for each of the provided intents. As such, it is critical that it
    34  // observes the engine in the same state that it was in when the intent keys
    35  // were originally collected. Because of this, callers are tasked with
    36  // indicating whether the intents were originally collected using a prefix
    37  // iterator or not.
    38  //
    39  // TODO(nvanbenschoten): remove the usePrefixIter complexity when we're fully on
    40  // Pebble and can guarantee that all iterators created from a read-only engine
    41  // are consistent.
    42  //
    43  // TODO(nvanbenschoten): mvccGetInternal should return the intent values
    44  // directly when reading at the READ_UNCOMMITTED consistency level. Since this
    45  // is only currently used for range lookups and when watching for a merge (both
    46  // of which are off the hot path), this is ok for now.
    47  func CollectIntentRows(
    48  	ctx context.Context, reader storage.Reader, usePrefixIter bool, intents []roachpb.Intent,
    49  ) ([]roachpb.KeyValue, error) {
    50  	if len(intents) == 0 {
    51  		return nil, nil
    52  	}
    53  	res := make([]roachpb.KeyValue, 0, len(intents))
    54  	for i := range intents {
    55  		kv, err := readProvisionalVal(ctx, reader, usePrefixIter, &intents[i])
    56  		if err != nil {
    57  			if errors.HasType(err, (*roachpb.WriteIntentError)(nil)) ||
    58  				errors.HasType(err, (*roachpb.ReadWithinUncertaintyIntervalError)(nil)) {
    59  				log.Fatalf(ctx, "unexpected %T in CollectIntentRows: %+v", err, err)
    60  			}
    61  			return nil, err
    62  		}
    63  		if kv.Value.IsPresent() {
    64  			res = append(res, kv)
    65  		}
    66  	}
    67  	return res, nil
    68  }
    69  
    70  // readProvisionalVal retrieves the provisional value for the provided intent
    71  // using the reader and the specified access method (i.e. with or without the
    72  // use of a prefix iterator). The function returns an empty KeyValue if the
    73  // intent is found to contain a deletion tombstone as its provisional value.
    74  func readProvisionalVal(
    75  	ctx context.Context, reader storage.Reader, usePrefixIter bool, intent *roachpb.Intent,
    76  ) (roachpb.KeyValue, error) {
    77  	if usePrefixIter {
    78  		val, _, err := storage.MVCCGetAsTxn(
    79  			ctx, reader, intent.Key, intent.Txn.WriteTimestamp, intent.Txn,
    80  		)
    81  		if err != nil {
    82  			return roachpb.KeyValue{}, err
    83  		}
    84  		if val == nil {
    85  			// Intent is a deletion.
    86  			return roachpb.KeyValue{}, nil
    87  		}
    88  		return roachpb.KeyValue{Key: intent.Key, Value: *val}, nil
    89  	}
    90  	res, err := storage.MVCCScanAsTxn(
    91  		ctx, reader, intent.Key, intent.Key.Next(), intent.Txn.WriteTimestamp, intent.Txn,
    92  	)
    93  	if err != nil {
    94  		return roachpb.KeyValue{}, err
    95  	}
    96  	if len(res.KVs) > 1 {
    97  		log.Fatalf(ctx, "multiple key-values returned from single-key scan: %+v", res.KVs)
    98  	} else if len(res.KVs) == 0 {
    99  		// Intent is a deletion.
   100  		return roachpb.KeyValue{}, nil
   101  	}
   102  	return res.KVs[0], nil
   103  
   104  }
   105  
   106  // acquireUnreplicatedLocksOnKeys adds an unreplicated lock acquisition by the
   107  // transaction to the provided result.Result for each key in the scan result.
   108  func acquireUnreplicatedLocksOnKeys(
   109  	res *result.Result,
   110  	txn *roachpb.Transaction,
   111  	scanFmt roachpb.ScanFormat,
   112  	scanRes *storage.MVCCScanResult,
   113  ) error {
   114  	res.Local.AcquiredLocks = make([]roachpb.LockAcquisition, scanRes.NumKeys)
   115  	switch scanFmt {
   116  	case roachpb.BATCH_RESPONSE:
   117  		var i int
   118  		return storage.MVCCScanDecodeKeyValues(scanRes.KVData, func(key storage.MVCCKey, _ []byte) error {
   119  			res.Local.AcquiredLocks[i] = roachpb.MakeLockAcquisition(txn, key.Key, lock.Unreplicated)
   120  			i++
   121  			return nil
   122  		})
   123  	case roachpb.KEY_VALUES:
   124  		for i, row := range scanRes.KVs {
   125  			res.Local.AcquiredLocks[i] = roachpb.MakeLockAcquisition(txn, row.Key, lock.Unreplicated)
   126  		}
   127  		return nil
   128  	default:
   129  		panic("unexpected scanFormat")
   130  	}
   131  }