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 }