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  }