github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/intent_test.go (about) 1 // Copyright 2020 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 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/roachpb" 18 "github.com/cockroachdb/cockroach/pkg/storage" 19 "github.com/cockroachdb/cockroach/pkg/testutils" 20 "github.com/cockroachdb/cockroach/pkg/util/hlc" 21 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 22 "github.com/stretchr/testify/require" 23 ) 24 25 // instrumentedEngine wraps a storage.Engine and allows for various methods in 26 // the interface to be instrumented for testing purposes. 27 type instrumentedEngine struct { 28 storage.Engine 29 30 onNewIterator func(storage.IterOptions) 31 // ... can be extended ... 32 } 33 34 func (ie *instrumentedEngine) NewIterator(opts storage.IterOptions) storage.Iterator { 35 if ie.onNewIterator != nil { 36 ie.onNewIterator(opts) 37 } 38 return ie.Engine.NewIterator(opts) 39 } 40 41 // TestCollectIntentsUsesSameIterator tests that all uses of CollectIntents 42 // (currently only by READ_UNCOMMITTED Gets, Scans, and ReverseScans) use the 43 // same cached iterator (prefix or non-prefix) for their initial read and their 44 // provisional value collection for any intents they find. 45 func TestCollectIntentsUsesSameIterator(t *testing.T) { 46 defer leaktest.AfterTest(t)() 47 48 ctx := context.Background() 49 key := roachpb.Key("key") 50 ts := hlc.Timestamp{WallTime: 123} 51 header := roachpb.Header{ 52 Timestamp: ts, 53 ReadConsistency: roachpb.READ_UNCOMMITTED, 54 } 55 56 testCases := []struct { 57 name string 58 run func(*testing.T, storage.ReadWriter) (intents []roachpb.KeyValue, _ error) 59 expPrefixIters int 60 expNonPrefixIters int 61 }{ 62 { 63 name: "get", 64 run: func(t *testing.T, db storage.ReadWriter) ([]roachpb.KeyValue, error) { 65 req := &roachpb.GetRequest{ 66 RequestHeader: roachpb.RequestHeader{Key: key}, 67 } 68 var resp roachpb.GetResponse 69 if _, err := Get(ctx, db, CommandArgs{Args: req, Header: header}, &resp); err != nil { 70 return nil, err 71 } 72 if resp.IntentValue == nil { 73 return nil, nil 74 } 75 return []roachpb.KeyValue{{Key: key, Value: *resp.IntentValue}}, nil 76 }, 77 expPrefixIters: 2, 78 expNonPrefixIters: 0, 79 }, 80 { 81 name: "scan", 82 run: func(t *testing.T, db storage.ReadWriter) ([]roachpb.KeyValue, error) { 83 req := &roachpb.ScanRequest{ 84 RequestHeader: roachpb.RequestHeader{Key: key, EndKey: key.Next()}, 85 } 86 var resp roachpb.ScanResponse 87 if _, err := Scan(ctx, db, CommandArgs{Args: req, Header: header}, &resp); err != nil { 88 return nil, err 89 } 90 return resp.IntentRows, nil 91 }, 92 expPrefixIters: 0, 93 expNonPrefixIters: 2, 94 }, 95 { 96 name: "reverse scan", 97 run: func(t *testing.T, db storage.ReadWriter) ([]roachpb.KeyValue, error) { 98 req := &roachpb.ReverseScanRequest{ 99 RequestHeader: roachpb.RequestHeader{Key: key, EndKey: key.Next()}, 100 } 101 var resp roachpb.ReverseScanResponse 102 if _, err := ReverseScan(ctx, db, CommandArgs{Args: req, Header: header}, &resp); err != nil { 103 return nil, err 104 } 105 return resp.IntentRows, nil 106 }, 107 expPrefixIters: 0, 108 expNonPrefixIters: 2, 109 }, 110 } 111 for _, c := range testCases { 112 t.Run(c.name, func(t *testing.T) { 113 // Test with and without deletion intents. If a READ_UNCOMMITTED request 114 // encounters an intent whose provisional value is a deletion tombstone, 115 // the request should ignore the intent and should not return any 116 // corresponding intent row. 117 testutils.RunTrueAndFalse(t, "deletion intent", func(t *testing.T, delete bool) { 118 db := &instrumentedEngine{Engine: storage.NewDefaultInMem()} 119 defer db.Close() 120 121 // Write an intent. 122 val := roachpb.MakeValueFromBytes([]byte("val")) 123 txn := roachpb.MakeTransaction("test", key, roachpb.NormalUserPriority, ts, 0) 124 var err error 125 if delete { 126 err = storage.MVCCDelete(ctx, db, nil, key, ts, &txn) 127 } else { 128 err = storage.MVCCPut(ctx, db, nil, key, ts, val, &txn) 129 } 130 require.NoError(t, err) 131 132 // Instrument iterator creation, count prefix vs. non-prefix iters. 133 var prefixIters, nonPrefixIters int 134 db.onNewIterator = func(opts storage.IterOptions) { 135 if opts.Prefix { 136 prefixIters++ 137 } else { 138 nonPrefixIters++ 139 } 140 } 141 142 intents, err := c.run(t, db) 143 require.NoError(t, err) 144 145 // Assert proper intent values. 146 if delete { 147 require.Len(t, intents, 0) 148 } else { 149 expIntentVal := val 150 expIntentVal.Timestamp = ts 151 expIntentKeyVal := roachpb.KeyValue{Key: key, Value: expIntentVal} 152 require.Len(t, intents, 1) 153 require.Equal(t, expIntentKeyVal, intents[0]) 154 } 155 156 // Assert proper iterator use. 157 require.Equal(t, c.expPrefixIters, prefixIters) 158 require.Equal(t, c.expNonPrefixIters, nonPrefixIters) 159 require.Equal(t, c.expNonPrefixIters, nonPrefixIters) 160 }) 161 }) 162 } 163 }