github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/intent_resolver_integration_test.go (about) 1 // Copyright 2016 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 "testing" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/kv" 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 21 "github.com/cockroachdb/cockroach/pkg/util/stop" 22 "github.com/cockroachdb/errors" 23 ) 24 25 func beginTransaction( 26 t *testing.T, store *Store, pri roachpb.UserPriority, key roachpb.Key, putKey bool, 27 ) *roachpb.Transaction { 28 txn := newTransaction("test", key, pri, store.Clock()) 29 if !putKey { 30 return txn 31 } 32 33 var ba roachpb.BatchRequest 34 ba.Header = roachpb.Header{Txn: txn} 35 put := putArgs(key, []byte("value")) 36 ba.Add(&put) 37 assignSeqNumsForReqs(txn, &put) 38 br, pErr := store.TestSender().Send(context.Background(), ba) 39 if pErr != nil { 40 t.Fatal(pErr) 41 } 42 return br.Txn 43 } 44 45 // TestContendedIntentWithDependencyCycle verifies that a queue of 46 // writers on a contended key will still notice a dependency cycle. 47 // In this case, txn3 writes "a", then txn1 writes "b" and "a", then 48 // txn2 writes "b", then txn3 writes "b". The deadlock is broken by 49 // an aborted transaction. 50 // 51 // Additional non-transactional reads on the same contended key are 52 // inserted to verify they do not interfere with writing transactions 53 // always pushing to ensure the dependency cycle can be detected. 54 // 55 // This test is something of an integration test which exercises the 56 // IntentResolver as well as the concurrency Manager's lockTable and 57 // txnWaitQueue. 58 func TestContendedIntentWithDependencyCycle(t *testing.T) { 59 defer leaktest.AfterTest(t)() 60 ctx := context.Background() 61 stopper := stop.NewStopper() 62 defer stopper.Stop(ctx) 63 store, _ := createTestStore(t, testStoreOpts{createSystemRanges: true}, stopper) 64 65 keyA := roachpb.Key("a") 66 keyB := roachpb.Key("b") 67 spanA := roachpb.Span{Key: keyA} 68 spanB := roachpb.Span{Key: keyB} 69 70 // Create the three transactions; at this point, none of them have 71 // conflicts. Txn1 has written "b", Txn3 has written "a". 72 txn1 := beginTransaction(t, store, -3, keyB, true /* putKey */) 73 txn2 := beginTransaction(t, store, -2, keyB, false /* putKey */) 74 txn3 := beginTransaction(t, store, -1, keyA, true /* putKey */) 75 76 // Send txn1 put, followed by an end transaction. 77 txnCh1 := make(chan error, 1) 78 go func() { 79 put := putArgs(keyA, []byte("value")) 80 assignSeqNumsForReqs(txn1, &put) 81 if _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{Txn: txn1}, &put); pErr != nil { 82 txnCh1 <- pErr.GoError() 83 return 84 } 85 et, _ := endTxnArgs(txn1, true) 86 et.LockSpans = []roachpb.Span{spanA, spanB} 87 et.CanCommitAtHigherTimestamp = true 88 assignSeqNumsForReqs(txn1, &et) 89 _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{Txn: txn1}, &et) 90 txnCh1 <- pErr.GoError() 91 }() 92 93 // Send a non-transactional read to keyB. This adds an early waiter 94 // to the intent resolver on keyB which txn2 must skip in order to 95 // properly register itself as a dependency by pushing txn1. 96 readCh1 := make(chan error, 1) 97 go func() { 98 get := getArgs(keyB) 99 _, pErr := kv.SendWrapped(ctx, store.TestSender(), &get) 100 readCh1 <- pErr.GoError() 101 }() 102 103 // Send txn2 put, followed by an end transaction. 104 txnCh2 := make(chan error, 1) 105 go func() { 106 put := putArgs(keyB, []byte("value")) 107 assignSeqNumsForReqs(txn2, &put) 108 repl, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{Txn: txn2}, &put) 109 if pErr != nil { 110 txnCh2 <- pErr.GoError() 111 return 112 } 113 txn2Copy := *repl.Header().Txn 114 txn2 = &txn2Copy 115 et, _ := endTxnArgs(txn2, true) 116 et.LockSpans = []roachpb.Span{spanB} 117 et.CanCommitAtHigherTimestamp = true 118 assignSeqNumsForReqs(txn2, &et) 119 _, pErr = kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{Txn: txn2}, &et) 120 txnCh2 <- pErr.GoError() 121 }() 122 123 // Send another non-transactional read to keyB to add a waiter in 124 // between txn2 and txn3. Txn3 must wait on txn2, instead of getting 125 // queued behind this reader, in order to establish the dependency cycle. 126 readCh2 := make(chan error, 1) 127 go func() { 128 get := getArgs(keyB) 129 _, pErr := kv.SendWrapped(ctx, store.TestSender(), &get) 130 readCh2 <- pErr.GoError() 131 }() 132 133 // Send txn3. Pause for 10ms to make it more likely that we have a 134 // dependency cycle of length 3, although we don't mind testing 135 // either way. 136 time.Sleep(10 * time.Millisecond) 137 txnCh3 := make(chan error, 1) 138 go func() { 139 put := putArgs(keyB, []byte("value")) 140 assignSeqNumsForReqs(txn3, &put) 141 _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{Txn: txn3}, &put) 142 txnCh3 <- pErr.GoError() 143 }() 144 145 // The third transaction will always be aborted. 146 err := <-txnCh3 147 if !errors.HasType(err, (*roachpb.UnhandledRetryableError)(nil)) { 148 t.Fatalf("expected transaction aborted error; got %T", err) 149 } 150 if err := <-txnCh1; err != nil { 151 t.Fatal(err) 152 } 153 if err := <-txnCh2; err != nil { 154 t.Fatal(err) 155 } 156 if err := <-readCh1; err != nil { 157 t.Fatal(err) 158 } 159 if err := <-readCh2; err != nil { 160 t.Fatal(err) 161 } 162 }