github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_resolve_intent_test.go (about) 1 // Copyright 2017 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 "fmt" 16 "strings" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/abortspan" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/storage" 23 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 24 "github.com/cockroachdb/cockroach/pkg/testutils" 25 "github.com/cockroachdb/cockroach/pkg/util/hlc" 26 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 27 "github.com/cockroachdb/cockroach/pkg/util/uuid" 28 "github.com/stretchr/testify/require" 29 ) 30 31 func TestDeclareKeysResolveIntent(t *testing.T) { 32 defer leaktest.AfterTest(t)() 33 34 const id = "f90b99de-6bd2-48a3-873c-12fdb9867a3c" 35 txnMeta := enginepb.TxnMeta{} 36 { 37 var err error 38 txnMeta.ID, err = uuid.FromString(id) 39 if err != nil { 40 t.Fatal(err) 41 } 42 } 43 abortSpanKey := fmt.Sprintf(`write local: /Local/RangeID/99/r/AbortSpan/"%s"`, id) 44 desc := roachpb.RangeDescriptor{ 45 RangeID: 99, 46 StartKey: roachpb.RKey("a"), 47 EndKey: roachpb.RKey("a"), 48 } 49 tests := []struct { 50 status roachpb.TransactionStatus 51 poison bool 52 expDeclares bool 53 }{ 54 { 55 status: roachpb.ABORTED, 56 poison: true, 57 expDeclares: true, 58 }, 59 { 60 status: roachpb.ABORTED, 61 poison: false, 62 expDeclares: true, 63 }, 64 { 65 status: roachpb.COMMITTED, 66 poison: true, 67 expDeclares: false, 68 }, 69 { 70 status: roachpb.COMMITTED, 71 poison: false, 72 expDeclares: false, 73 }, 74 } 75 ctx := context.Background() 76 engine := storage.NewDefaultInMem() 77 defer engine.Close() 78 testutils.RunTrueAndFalse(t, "ranged", func(t *testing.T, ranged bool) { 79 for _, test := range tests { 80 t.Run("", func(t *testing.T) { 81 ri := roachpb.ResolveIntentRequest{ 82 IntentTxn: txnMeta, 83 Status: test.status, 84 Poison: test.poison, 85 } 86 ri.Key = roachpb.Key("b") 87 rir := roachpb.ResolveIntentRangeRequest{ 88 IntentTxn: ri.IntentTxn, 89 Status: ri.Status, 90 Poison: ri.Poison, 91 } 92 rir.Key = ri.Key 93 rir.EndKey = roachpb.Key("c") 94 95 as := abortspan.New(desc.RangeID) 96 97 var latchSpans, lockSpans spanset.SpanSet 98 batch := engine.NewBatch() 99 batch = spanset.NewBatch(batch, &latchSpans) 100 defer batch.Close() 101 102 var h roachpb.Header 103 h.RangeID = desc.RangeID 104 105 cArgs := CommandArgs{Header: h} 106 cArgs.EvalCtx = (&MockEvalCtx{AbortSpan: as}).EvalContext() 107 108 if !ranged { 109 cArgs.Args = &ri 110 declareKeysResolveIntent(&desc, h, &ri, &latchSpans, &lockSpans) 111 if _, err := ResolveIntent(ctx, batch, cArgs, &roachpb.ResolveIntentResponse{}); err != nil { 112 t.Fatal(err) 113 } 114 } else { 115 cArgs.Args = &rir 116 declareKeysResolveIntentRange(&desc, h, &rir, &latchSpans, &lockSpans) 117 if _, err := ResolveIntentRange(ctx, batch, cArgs, &roachpb.ResolveIntentRangeResponse{}); err != nil { 118 t.Fatal(err) 119 } 120 } 121 122 if s := latchSpans.String(); strings.Contains(s, abortSpanKey) != test.expDeclares { 123 t.Errorf("expected AbortSpan declared: %t, but got spans\n%s", test.expDeclares, s) 124 } 125 if !lockSpans.Empty() { 126 t.Errorf("expected no lock spans declared, but got spans\n%s", lockSpans.String()) 127 } 128 }) 129 } 130 }) 131 } 132 133 // TestResolveIntentAfterPartialRollback checks that the ResolveIntent 134 // and ResolveIntentRange properly propagate their IgnoredSeqNums 135 // parameter to the MVCC layer and only commit writes at non-ignored 136 // seqnums. 137 func TestResolveIntentAfterPartialRollback(t *testing.T) { 138 defer leaktest.AfterTest(t)() 139 140 ctx := context.Background() 141 k := roachpb.Key("a") 142 ts := hlc.Timestamp{WallTime: 1} 143 ts2 := hlc.Timestamp{WallTime: 2} 144 endKey := roachpb.Key("z") 145 txn := roachpb.MakeTransaction("test", k, 0, ts, 0) 146 desc := roachpb.RangeDescriptor{ 147 RangeID: 99, 148 StartKey: roachpb.RKey(k), 149 EndKey: roachpb.RKey(endKey), 150 } 151 152 testutils.RunTrueAndFalse(t, "ranged", func(t *testing.T, ranged bool) { 153 db := storage.NewDefaultInMem() 154 defer db.Close() 155 batch := db.NewBatch() 156 defer batch.Close() 157 158 var v roachpb.Value 159 // Write a first value at key. 160 v.SetString("a") 161 txn.Sequence = 0 162 if err := storage.MVCCPut(ctx, batch, nil, k, ts, v, &txn); err != nil { 163 t.Fatal(err) 164 } 165 // Write another value. 166 v.SetString("b") 167 txn.Sequence = 1 168 if err := storage.MVCCPut(ctx, batch, nil, k, ts, v, &txn); err != nil { 169 t.Fatal(err) 170 } 171 if err := batch.Commit(true); err != nil { 172 t.Fatal(err) 173 } 174 175 // Partially revert the 2nd store above. 176 ignoredSeqNums := []enginepb.IgnoredSeqNumRange{{Start: 1, End: 1}} 177 178 h := roachpb.Header{ 179 RangeID: desc.RangeID, 180 Timestamp: ts, 181 } 182 183 var spans spanset.SpanSet 184 rbatch := db.NewBatch() 185 rbatch = spanset.NewBatch(rbatch, &spans) 186 defer rbatch.Close() 187 188 if !ranged { 189 // Resolve a point intent. 190 ri := roachpb.ResolveIntentRequest{ 191 IntentTxn: txn.TxnMeta, 192 Status: roachpb.COMMITTED, 193 IgnoredSeqNums: ignoredSeqNums, 194 } 195 ri.Key = k 196 197 declareKeysResolveIntent(&desc, h, &ri, &spans, nil) 198 199 if _, err := ResolveIntent(ctx, rbatch, 200 CommandArgs{ 201 Header: h, 202 EvalCtx: (&MockEvalCtx{}).EvalContext(), 203 Args: &ri, 204 }, 205 &roachpb.ResolveIntentResponse{}, 206 ); err != nil { 207 t.Fatal(err) 208 } 209 } else { 210 // Resolve an intent range. 211 rir := roachpb.ResolveIntentRangeRequest{ 212 IntentTxn: txn.TxnMeta, 213 Status: roachpb.COMMITTED, 214 IgnoredSeqNums: ignoredSeqNums, 215 } 216 rir.Key = k 217 rir.EndKey = endKey 218 219 declareKeysResolveIntentRange(&desc, h, &rir, &spans, nil) 220 221 h.MaxSpanRequestKeys = 10 222 if _, err := ResolveIntentRange(ctx, rbatch, 223 CommandArgs{ 224 Header: h, 225 EvalCtx: (&MockEvalCtx{}).EvalContext(), 226 Args: &rir, 227 }, 228 &roachpb.ResolveIntentRangeResponse{}, 229 ); err != nil { 230 t.Fatal(err) 231 } 232 } 233 234 if err := rbatch.Commit(true); err != nil { 235 t.Fatal(err) 236 } 237 238 batch = db.NewBatch() 239 defer batch.Close() 240 241 // The second write has been rolled back; verify that the remaining 242 // value is from the first write. 243 res, i, err := storage.MVCCGet(ctx, batch, k, ts2, storage.MVCCGetOptions{}) 244 if err != nil { 245 t.Fatal(err) 246 } 247 if i != nil { 248 t.Errorf("%s: found intent, expected none: %+v", k, i) 249 } 250 if res == nil { 251 t.Errorf("%s: no value found, expected one", k) 252 } else { 253 s, err := res.GetBytes() 254 if err != nil { 255 t.Fatal(err) 256 } 257 require.Equal(t, "a", string(s), "at key %s", k) 258 } 259 }) 260 }