github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvclient/kvcoord/txn_coord_sender_test.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 kvcoord 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "reflect" 18 "strconv" 19 "sync/atomic" 20 "testing" 21 "time" 22 23 "github.com/cockroachdb/cockroach/pkg/kv" 24 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 25 "github.com/cockroachdb/cockroach/pkg/roachpb" 26 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 27 "github.com/cockroachdb/cockroach/pkg/storage" 28 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 29 "github.com/cockroachdb/cockroach/pkg/testutils" 30 "github.com/cockroachdb/cockroach/pkg/testutils/localtestcluster" 31 "github.com/cockroachdb/cockroach/pkg/util/hlc" 32 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 33 "github.com/cockroachdb/cockroach/pkg/util/log" 34 "github.com/cockroachdb/cockroach/pkg/util/metric" 35 "github.com/cockroachdb/cockroach/pkg/util/stop" 36 "github.com/cockroachdb/cockroach/pkg/util/tracing" 37 "github.com/cockroachdb/errors" 38 "github.com/stretchr/testify/require" 39 "golang.org/x/sync/errgroup" 40 ) 41 42 func strToValue(s string) *roachpb.Value { 43 v := roachpb.MakeValueFromBytes([]byte(s)) 44 return &v 45 } 46 47 // createTestDB creates a local test server and starts it. The caller 48 // is responsible for stopping the test server. 49 func createTestDB(t testing.TB) *localtestcluster.LocalTestCluster { 50 return createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), nil) 51 } 52 53 func createTestDBWithContextAndKnobs( 54 t testing.TB, dbCtx kv.DBContext, knobs *kvserver.StoreTestingKnobs, 55 ) *localtestcluster.LocalTestCluster { 56 s := &localtestcluster.LocalTestCluster{ 57 DBContext: &dbCtx, 58 StoreTestingKnobs: knobs, 59 } 60 s.Start(t, testutils.NewNodeTestBaseContext(), InitFactoryForLocalTestCluster) 61 return s 62 } 63 64 // makeTS creates a new timestamp. 65 func makeTS(walltime int64, logical int32) hlc.Timestamp { 66 return hlc.Timestamp{ 67 WallTime: walltime, 68 Logical: logical, 69 } 70 } 71 72 // TestTxnCoordSenderBeginTransaction verifies that a command sent with a 73 // not-nil Txn with empty ID gets a new transaction initialized. 74 func TestTxnCoordSenderBeginTransaction(t *testing.T) { 75 defer leaktest.AfterTest(t)() 76 s := createTestDB(t) 77 defer s.Stop() 78 ctx := context.Background() 79 80 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 81 82 // Put request will create a new transaction. 83 key := roachpb.Key("key") 84 txn.TestingSetPriority(10) 85 txn.SetDebugName("test txn") 86 if err := txn.Put(ctx, key, []byte("value")); err != nil { 87 t.Fatal(err) 88 } 89 proto := txn.TestingCloneTxn() 90 if proto.Name != "test txn" { 91 t.Errorf("expected txn name to be %q; got %q", "test txn", proto.Name) 92 } 93 if proto.Priority != 10 { 94 t.Errorf("expected txn priority 10; got %d", proto.Priority) 95 } 96 if !bytes.Equal(proto.Key, key) { 97 t.Errorf("expected txn Key to match %q != %q", key, proto.Key) 98 } 99 } 100 101 // TestTxnCoordSenderKeyRanges verifies that multiple requests to same or 102 // overlapping key ranges causes the coordinator to keep track only of 103 // the minimum number of ranges. 104 func TestTxnCoordSenderKeyRanges(t *testing.T) { 105 defer leaktest.AfterTest(t)() 106 107 ctx := context.Background() 108 ranges := []struct { 109 start, end roachpb.Key 110 }{ 111 {roachpb.Key("a"), roachpb.Key(nil)}, 112 {roachpb.Key("a"), roachpb.Key(nil)}, 113 {roachpb.Key("aa"), roachpb.Key(nil)}, 114 {roachpb.Key("b"), roachpb.Key(nil)}, 115 {roachpb.Key("aa"), roachpb.Key("c")}, 116 {roachpb.Key("b"), roachpb.Key("c")}, 117 } 118 119 s := createTestDB(t) 120 defer s.Stop() 121 122 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 123 // Disable txn pipelining so that all write spans are immediately 124 // added to the transaction's lock footprint. 125 if err := txn.DisablePipelining(); err != nil { 126 t.Fatal(err) 127 } 128 tc := txn.Sender().(*TxnCoordSender) 129 130 for _, rng := range ranges { 131 if rng.end != nil { 132 if err := txn.DelRange(ctx, rng.start, rng.end); err != nil { 133 t.Fatal(err) 134 } 135 } else { 136 if err := txn.Put(ctx, rng.start, []byte("value")); err != nil { 137 t.Fatal(err) 138 } 139 } 140 } 141 142 // Verify that the transaction coordinator is only tracking two lock 143 // spans. "a" and range "aa"-"c". 144 tc.interceptorAlloc.txnPipeliner.lockFootprint.mergeAndSort() 145 lockSpans := tc.interceptorAlloc.txnPipeliner.lockFootprint.asSlice() 146 if len(lockSpans) != 2 { 147 t.Errorf("expected 2 entries in keys range group; got %v", lockSpans) 148 } 149 } 150 151 // TestTxnCoordSenderCondenseLockSpans verifies that lock spans are condensed 152 // along range boundaries when they exceed the maximum intent bytes threshold. 153 func TestTxnCoordSenderCondenseLockSpans(t *testing.T) { 154 defer leaktest.AfterTest(t)() 155 a := roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key(nil)} 156 b := roachpb.Span{Key: roachpb.Key("b"), EndKey: roachpb.Key(nil)} 157 c := roachpb.Span{Key: roachpb.Key("c"), EndKey: roachpb.Key(nil)} 158 d := roachpb.Span{Key: roachpb.Key("dddddd"), EndKey: roachpb.Key(nil)} 159 e := roachpb.Span{Key: roachpb.Key("e"), EndKey: roachpb.Key(nil)} 160 aToBClosed := roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("b").Next()} 161 cToEClosed := roachpb.Span{Key: roachpb.Key("c"), EndKey: roachpb.Key("e").Next()} 162 fTof0 := roachpb.Span{Key: roachpb.Key("f"), EndKey: roachpb.Key("f0")} 163 g := roachpb.Span{Key: roachpb.Key("g"), EndKey: roachpb.Key(nil)} 164 g0Tog1 := roachpb.Span{Key: roachpb.Key("g0"), EndKey: roachpb.Key("g1")} 165 fTog1Closed := roachpb.Span{Key: roachpb.Key("f"), EndKey: roachpb.Key("g1")} 166 testCases := []struct { 167 span roachpb.Span 168 expLocks []roachpb.Span 169 expLocksSize int64 170 }{ 171 {span: a, expLocks: []roachpb.Span{a}, expLocksSize: 1}, 172 {span: b, expLocks: []roachpb.Span{a, b}, expLocksSize: 2}, 173 {span: c, expLocks: []roachpb.Span{a, b, c}, expLocksSize: 3}, 174 {span: d, expLocks: []roachpb.Span{a, b, c, d}, expLocksSize: 9}, 175 // Note that c-e condenses and then lists first. 176 {span: e, expLocks: []roachpb.Span{cToEClosed, a, b}, expLocksSize: 5}, 177 {span: fTof0, expLocks: []roachpb.Span{cToEClosed, a, b, fTof0}, expLocksSize: 8}, 178 {span: g, expLocks: []roachpb.Span{cToEClosed, a, b, fTof0, g}, expLocksSize: 9}, 179 {span: g0Tog1, expLocks: []roachpb.Span{fTog1Closed, cToEClosed, aToBClosed}, expLocksSize: 9}, 180 // Add a key in the middle of a span, which will get merged on commit. 181 {span: c, expLocks: []roachpb.Span{aToBClosed, cToEClosed, fTog1Closed}, expLocksSize: 9}, 182 } 183 splits := []roachpb.Span{ 184 {Key: roachpb.Key("a"), EndKey: roachpb.Key("c")}, 185 {Key: roachpb.Key("c"), EndKey: roachpb.Key("f")}, 186 {Key: roachpb.Key("f"), EndKey: roachpb.Key("j")}, 187 } 188 descs := []roachpb.RangeDescriptor{testMetaRangeDescriptor} 189 for i, s := range splits { 190 descs = append(descs, roachpb.RangeDescriptor{ 191 RangeID: roachpb.RangeID(2 + i), 192 StartKey: roachpb.RKey(s.Key), 193 EndKey: roachpb.RKey(s.EndKey), 194 InternalReplicas: []roachpb.ReplicaDescriptor{{NodeID: 1, StoreID: 1}}, 195 }) 196 } 197 descDB := mockRangeDescriptorDBForDescs(descs...) 198 s := createTestDB(t) 199 st := s.Store.ClusterSettings() 200 trackedWritesMaxSize.Override(&st.SV, 10) /* 10 bytes and it will condense */ 201 defer s.Stop() 202 203 // Check end transaction locks, which should be condensed and split 204 // at range boundaries. 205 expLocks := []roachpb.Span{aToBClosed, cToEClosed, fTog1Closed} 206 var sendFn simpleSendFn = func( 207 _ context.Context, _ SendOptions, _ ReplicaSlice, args roachpb.BatchRequest, 208 ) (*roachpb.BatchResponse, error) { 209 resp := args.CreateReply() 210 resp.Txn = args.Txn 211 if req, ok := args.GetArg(roachpb.EndTxn); ok { 212 if !req.(*roachpb.EndTxnRequest).Commit { 213 t.Errorf("expected commit to be true") 214 } 215 et := req.(*roachpb.EndTxnRequest) 216 if a, e := et.LockSpans, expLocks; !reflect.DeepEqual(a, e) { 217 t.Errorf("expected end transaction to have locks %+v; got %+v", e, a) 218 } 219 resp.Txn.Status = roachpb.COMMITTED 220 } 221 return resp, nil 222 } 223 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 224 ds := NewDistSender( 225 DistSenderConfig{ 226 AmbientCtx: ambient, 227 Clock: s.Clock, 228 RPCContext: s.Cfg.RPCContext, 229 TestingKnobs: ClientTestingKnobs{ 230 TransportFactory: adaptSimpleTransport(sendFn), 231 }, 232 RangeDescriptorDB: descDB, 233 Settings: cluster.MakeTestingClusterSettings(), 234 }, 235 s.Gossip, 236 ) 237 tsf := NewTxnCoordSenderFactory( 238 TxnCoordSenderFactoryConfig{ 239 AmbientCtx: ambient, 240 Settings: st, 241 Clock: s.Clock, 242 Stopper: s.Stopper, 243 }, 244 ds, 245 ) 246 db := kv.NewDB(ambient, tsf, s.Clock) 247 ctx := context.Background() 248 249 txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */) 250 // Disable txn pipelining so that all write spans are immediately 251 // added to the transaction's lock footprint. 252 if err := txn.DisablePipelining(); err != nil { 253 t.Fatal(err) 254 } 255 for i, tc := range testCases { 256 if tc.span.EndKey != nil { 257 if err := txn.DelRange(ctx, tc.span.Key, tc.span.EndKey); err != nil { 258 t.Fatal(err) 259 } 260 } else { 261 if err := txn.Put(ctx, tc.span.Key, []byte("value")); err != nil { 262 t.Fatal(err) 263 } 264 } 265 tcs := txn.Sender().(*TxnCoordSender) 266 locks := tcs.interceptorAlloc.txnPipeliner.lockFootprint.asSlice() 267 if a, e := locks, tc.expLocks; !reflect.DeepEqual(a, e) { 268 t.Errorf("%d: expected keys %+v; got %+v", i, e, a) 269 } 270 locksSize := int64(0) 271 for _, i := range locks { 272 locksSize += int64(len(i.Key) + len(i.EndKey)) 273 } 274 if a, e := locksSize, tc.expLocksSize; a != e { 275 t.Errorf("%d: keys size expected %d; got %d", i, e, a) 276 } 277 } 278 if err := txn.Commit(ctx); err != nil { 279 t.Fatal(err) 280 } 281 } 282 283 // Test that the theartbeat loop detects aborted transactions and stops. 284 func TestTxnCoordSenderHeartbeat(t *testing.T) { 285 defer leaktest.AfterTest(t)() 286 s := createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), &kvserver.StoreTestingKnobs{ 287 DisableScanner: true, 288 DisableSplitQueue: true, 289 DisableMergeQueue: true, 290 }) 291 defer s.Stop() 292 ctx := context.Background() 293 294 keyA := roachpb.Key("a") 295 keyC := roachpb.Key("c") 296 splitKey := roachpb.Key("b") 297 if err := s.DB.AdminSplit(ctx, splitKey /* spanKey */, splitKey /* splitKey */, hlc.MaxTimestamp /* expirationTimestamp */); err != nil { 298 t.Fatal(err) 299 } 300 301 // Make a db with a short heartbeat interval. 302 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 303 tsf := NewTxnCoordSenderFactory( 304 TxnCoordSenderFactoryConfig{ 305 AmbientCtx: ambient, 306 // Short heartbeat interval. 307 HeartbeatInterval: time.Millisecond, 308 Settings: s.Cfg.Settings, 309 Clock: s.Clock, 310 Stopper: s.Stopper, 311 }, 312 NewDistSenderForLocalTestCluster( 313 s.Cfg.Settings, &roachpb.NodeDescriptor{NodeID: 1}, 314 ambient.Tracer, s.Clock, s.Latency, s.Stores, s.Stopper, s.Gossip, 315 ), 316 ) 317 quickHeartbeatDB := kv.NewDB(ambient, tsf, s.Clock) 318 319 // We're going to test twice. In both cases the heartbeat is supposed to 320 // notice that its transaction is aborted, but: 321 // - once the abort span is populated on the txn's range. 322 // - once the abort span is not populated. 323 // The two conditions are created by either clearing an intent from the txn's 324 // range or not (i.e. clearing an intent from another range). 325 // The difference is supposed to be immaterial for the heartbeat loop (that's 326 // what we're testing). As of June 2018, HeartbeatTxnRequests don't check the 327 // abort span. 328 for _, pusherKey := range []roachpb.Key{keyA, keyC} { 329 t.Run(fmt.Sprintf("pusher:%s", pusherKey), func(t *testing.T) { 330 // Make a db with a short heartbeat interval. 331 initialTxn := kv.NewTxn(ctx, quickHeartbeatDB, 0 /* gatewayNodeID */) 332 tc := initialTxn.Sender().(*TxnCoordSender) 333 334 if err := initialTxn.Put(ctx, keyA, []byte("value")); err != nil { 335 t.Fatal(err) 336 } 337 if err := initialTxn.Put(ctx, keyC, []byte("value")); err != nil { 338 t.Fatal(err) 339 } 340 341 // Verify 3 heartbeats. 342 var heartbeatTS hlc.Timestamp 343 for i := 0; i < 3; i++ { 344 testutils.SucceedsSoon(t, func() error { 345 txn, pErr := getTxn(ctx, initialTxn) 346 if pErr != nil { 347 t.Fatal(pErr) 348 } 349 // Advance clock by 1ns. 350 s.Manual.Increment(1) 351 if lastActive := txn.LastActive(); heartbeatTS.Less(lastActive) { 352 heartbeatTS = lastActive 353 return nil 354 } 355 return errors.Errorf("expected heartbeat") 356 }) 357 } 358 359 // Push our txn with another high-priority txn. 360 { 361 if err := s.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 362 if err := txn.SetUserPriority(roachpb.MaxUserPriority); err != nil { 363 return err 364 } 365 return txn.Put(ctx, pusherKey, []byte("pusher val")) 366 }); err != nil { 367 t.Fatal(err) 368 } 369 } 370 371 // Verify that the abort is discovered and the heartbeat discontinued. 372 // This relies on the heartbeat loop stopping once it figures out that the txn 373 // has been aborted. 374 testutils.SucceedsSoon(t, func() error { 375 if tc.IsTracking() { 376 return fmt.Errorf("transaction is not aborted") 377 } 378 return nil 379 }) 380 381 // Trying to do something else should give us a TransactionAbortedError. 382 _, err := initialTxn.Get(ctx, "a") 383 assertTransactionAbortedError(t, err) 384 }) 385 } 386 } 387 388 // getTxn fetches the requested key and returns the transaction info. 389 func getTxn(ctx context.Context, txn *kv.Txn) (*roachpb.Transaction, *roachpb.Error) { 390 txnMeta := txn.TestingCloneTxn().TxnMeta 391 qt := &roachpb.QueryTxnRequest{ 392 RequestHeader: roachpb.RequestHeader{ 393 Key: txnMeta.Key, 394 }, 395 Txn: txnMeta, 396 } 397 398 ba := roachpb.BatchRequest{} 399 ba.Timestamp = txnMeta.WriteTimestamp 400 ba.Add(qt) 401 402 db := txn.DB() 403 sender := db.NonTransactionalSender() 404 405 br, pErr := sender.Send(ctx, ba) 406 if pErr != nil { 407 return nil, pErr 408 } 409 return &br.Responses[0].GetInner().(*roachpb.QueryTxnResponse).QueriedTxn, nil 410 } 411 412 func verifyCleanup(key roachpb.Key, eng storage.Engine, t *testing.T, coords ...*TxnCoordSender) { 413 testutils.SucceedsSoon(t, func() error { 414 for _, coord := range coords { 415 if coord.IsTracking() { 416 return fmt.Errorf("expected no heartbeat") 417 } 418 } 419 meta := &enginepb.MVCCMetadata{} 420 //lint:ignore SA1019 historical usage of deprecated eng.GetProto is OK 421 ok, _, _, err := eng.GetProto(storage.MakeMVCCMetadataKey(key), meta) 422 if err != nil { 423 return fmt.Errorf("error getting MVCC metadata: %s", err) 424 } 425 if ok && meta.Txn != nil { 426 return fmt.Errorf("found unexpected write intent: %s", meta) 427 } 428 return nil 429 }) 430 } 431 432 // TestTxnCoordSenderEndTxn verifies that ending a transaction 433 // sends resolve write intent requests. 434 func TestTxnCoordSenderEndTxn(t *testing.T) { 435 defer leaktest.AfterTest(t)() 436 s := createTestDB(t) 437 defer s.Stop() 438 ctx := context.Background() 439 440 // 4 cases: no deadline, past deadline, equal deadline, future deadline. 441 for i := 0; i < 4; i++ { 442 key := roachpb.Key("key: " + strconv.Itoa(i)) 443 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 444 // Initialize the transaction. 445 if pErr := txn.Put(ctx, key, []byte("value")); pErr != nil { 446 t.Fatal(pErr) 447 } 448 // Conflicting transaction that pushes the above transaction. 449 conflictTxn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 450 conflictTxn.TestingSetPriority(enginepb.MaxTxnPriority) 451 if _, pErr := conflictTxn.Get(ctx, key); pErr != nil { 452 t.Fatal(pErr) 453 } 454 455 // The transaction was pushed at least to conflictTxn's timestamp (but 456 // it could have been pushed more - the push takes a timestamp off the 457 // HLC). 458 pusheeTxn, pErr := getTxn(ctx, txn) 459 if pErr != nil { 460 t.Fatal(pErr) 461 } 462 pushedTimestamp := pusheeTxn.WriteTimestamp 463 464 { 465 var err error 466 switch i { 467 case 0: 468 // No deadline. 469 470 case 1: 471 // Past deadline. 472 if !txn.UpdateDeadlineMaybe(ctx, pushedTimestamp.Prev()) { 473 t.Fatalf("did not update deadline") 474 } 475 476 case 2: 477 // Equal deadline. 478 if !txn.UpdateDeadlineMaybe(ctx, pushedTimestamp) { 479 t.Fatalf("did not update deadline") 480 } 481 482 case 3: 483 // Future deadline. 484 485 if !txn.UpdateDeadlineMaybe(ctx, pushedTimestamp.Next()) { 486 t.Fatalf("did not update deadline") 487 } 488 } 489 err = txn.CommitOrCleanup(ctx) 490 491 switch i { 492 case 0: 493 // No deadline. 494 if err != nil { 495 t.Fatal(err) 496 } 497 498 case 1: 499 // Past deadline. 500 fallthrough 501 case 2: 502 // Equal deadline. 503 assertTransactionRetryError(t, err) 504 if !testutils.IsError(err, "RETRY_COMMIT_DEADLINE_EXCEEDED") { 505 t.Fatalf("expected deadline exceeded, got: %s", err) 506 } 507 case 3: 508 // Future deadline. 509 if err != nil { 510 t.Fatal(err) 511 } 512 } 513 } 514 verifyCleanup(key, s.Eng, t, txn.Sender().(*TxnCoordSender)) 515 } 516 } 517 518 // TestTxnCoordSenderAddLockOnError verifies that locks are tracked if the 519 // transaction is, even on error. 520 func TestTxnCoordSenderAddLockOnError(t *testing.T) { 521 defer leaktest.AfterTest(t)() 522 s := createTestDB(t) 523 defer s.Stop() 524 525 ctx := context.Background() 526 527 // Create a transaction with intent at "x". 528 key := roachpb.Key("x") 529 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 530 tc := txn.Sender().(*TxnCoordSender) 531 532 // Write so that the coordinator begins tracking this txn. 533 if err := txn.Put(ctx, "x", "y"); err != nil { 534 t.Fatal(err) 535 } 536 { 537 err := txn.CPut(ctx, key, []byte("x"), strToValue("born to fail")) 538 if !errors.HasType(err, (*roachpb.ConditionFailedError)(nil)) { 539 t.Fatal(err) 540 } 541 } 542 tc.interceptorAlloc.txnPipeliner.lockFootprint.mergeAndSort() 543 lockSpans := tc.interceptorAlloc.txnPipeliner.lockFootprint.asSlice() 544 expSpans := []roachpb.Span{{Key: key, EndKey: []byte("")}} 545 equal := !reflect.DeepEqual(lockSpans, expSpans) 546 if err := txn.Rollback(ctx); err != nil { 547 t.Fatal(err) 548 } 549 if !equal { 550 t.Fatalf("expected stored locks %v, got %v", expSpans, lockSpans) 551 } 552 } 553 554 func assertTransactionRetryError(t *testing.T, e error) { 555 t.Helper() 556 if retErr := (*roachpb.TransactionRetryWithProtoRefreshError)(nil); errors.As(e, &retErr) { 557 if !testutils.IsError(retErr, "TransactionRetryError") { 558 t.Fatalf("expected the cause to be TransactionRetryError, but got %s", 559 retErr) 560 } 561 } else { 562 t.Fatalf("expected a retryable error, but got %s (%T)", e, e) 563 } 564 } 565 566 func assertTransactionAbortedError(t *testing.T, e error) { 567 if retErr := (*roachpb.TransactionRetryWithProtoRefreshError)(nil); errors.As(e, &retErr) { 568 if !testutils.IsError(retErr, "TransactionAbortedError") { 569 t.Fatalf("expected the cause to be TransactionAbortedError, but got %s", 570 retErr) 571 } 572 } else { 573 t.Fatalf("expected a retryable error, but got %s (%T)", e, e) 574 } 575 } 576 577 // TestTxnCoordSenderCleanupOnAborted verifies that if a txn receives a 578 // TransactionAbortedError, the coordinator cleans up the transaction. 579 func TestTxnCoordSenderCleanupOnAborted(t *testing.T) { 580 defer leaktest.AfterTest(t)() 581 s := createTestDB(t) 582 defer s.Stop() 583 ctx := context.Background() 584 585 // Create a transaction with intent at "a". 586 key := roachpb.Key("a") 587 txn1 := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 588 if err := txn1.Put(ctx, key, []byte("value")); err != nil { 589 t.Fatal(err) 590 } 591 592 // Push the transaction (by writing key "a" with higher priority) to abort it. 593 txn2 := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 594 if err := txn2.SetUserPriority(roachpb.MaxUserPriority); err != nil { 595 t.Fatal(err) 596 } 597 if err := txn2.Put(ctx, key, []byte("value2")); err != nil { 598 t.Fatal(err) 599 } 600 601 // Now end the transaction and verify we've cleanup up, even though 602 // end transaction failed. 603 err := txn1.CommitOrCleanup(ctx) 604 assertTransactionAbortedError(t, err) 605 if err := txn2.CommitOrCleanup(ctx); err != nil { 606 t.Fatal(err) 607 } 608 verifyCleanup(key, s.Eng, t, txn1.Sender().(*TxnCoordSender), txn2.Sender().(*TxnCoordSender)) 609 } 610 611 // TestTxnCoordSenderCleanupOnCommitAfterRestart verifies that if a txn restarts 612 // at a higher epoch and then commits before it has acquired any locks in the new 613 // epoch, the coordinator still cleans up the transaction. In #40466, we saw that 614 // this case could be detected as a 1PC transaction and the cleanup during the 615 // commit could be omitted. 616 func TestTxnCoordSenderCleanupOnCommitAfterRestart(t *testing.T) { 617 defer leaktest.AfterTest(t)() 618 s := createTestDB(t) 619 defer s.Stop() 620 ctx := context.Background() 621 622 // Create a transaction with intent at "a". 623 key := roachpb.Key("a") 624 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 625 if err := txn.Put(ctx, key, []byte("value")); err != nil { 626 t.Fatal(err) 627 } 628 629 // Restart the transaction with a new epoch. 630 txn.ManualRestart(ctx, s.Clock.Now()) 631 632 // Now immediately commit. 633 if err := txn.CommitOrCleanup(ctx); err != nil { 634 t.Fatal(err) 635 } 636 verifyCleanup(key, s.Eng, t, txn.Sender().(*TxnCoordSender)) 637 } 638 639 // TestTxnCoordSenderGCWithAmbiguousResultErr verifies that the coordinator 640 // cleans up extant transactions and locks after an ambiguous result error is 641 // observed, even if the error is on the first request. 642 func TestTxnCoordSenderGCWithAmbiguousResultErr(t *testing.T) { 643 defer leaktest.AfterTest(t)() 644 645 testutils.RunTrueAndFalse(t, "errOnFirst", func(t *testing.T, errOnFirst bool) { 646 key := roachpb.Key("a") 647 are := roachpb.NewAmbiguousResultError("very ambiguous") 648 knobs := &kvserver.StoreTestingKnobs{ 649 TestingResponseFilter: func(ctx context.Context, ba roachpb.BatchRequest, br *roachpb.BatchResponse) *roachpb.Error { 650 for _, req := range ba.Requests { 651 if putReq, ok := req.GetInner().(*roachpb.PutRequest); ok && putReq.Key.Equal(key) { 652 return roachpb.NewError(are) 653 } 654 } 655 return nil 656 }, 657 } 658 659 s := createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), knobs) 660 defer s.Stop() 661 662 ctx := context.Background() 663 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 664 tc := txn.Sender().(*TxnCoordSender) 665 if !errOnFirst { 666 otherKey := roachpb.Key("other") 667 if err := txn.Put(ctx, otherKey, []byte("value")); err != nil { 668 t.Fatal(err) 669 } 670 } 671 if err := txn.Put(ctx, key, []byte("value")); !testutils.IsError(err, "result is ambiguous") { 672 t.Fatalf("expected error %v, found %v", are, err) 673 } 674 675 if err := txn.Rollback(ctx); err != nil { 676 t.Fatal(err) 677 } 678 679 testutils.SucceedsSoon(t, func() error { 680 // Locking the TxnCoordSender to prevent a data race. 681 if tc.IsTracking() { 682 return errors.Errorf("expected garbage collection") 683 } 684 return nil 685 }) 686 687 verifyCleanup(key, s.Eng, t, tc) 688 }) 689 } 690 691 // TestTxnCoordSenderTxnUpdatedOnError verifies that errors adjust the 692 // response transaction's timestamp and priority as appropriate. 693 func TestTxnCoordSenderTxnUpdatedOnError(t *testing.T) { 694 defer leaktest.AfterTest(t)() 695 ctx := context.Background() 696 origTS := makeTS(123, 0) 697 plus10 := origTS.Add(10, 10) 698 plus20 := origTS.Add(20, 0) 699 testCases := []struct { 700 // The test's name. 701 name string 702 pErrGen func(txn *roachpb.Transaction) *roachpb.Error 703 expEpoch enginepb.TxnEpoch 704 expPri enginepb.TxnPriority 705 expTS, expOrigTS hlc.Timestamp 706 // Is set, we're expecting that the Transaction proto is re-initialized (as 707 // opposed to just having the epoch incremented). 708 expNewTransaction bool 709 nodeSeen bool 710 }{ 711 { 712 // No error, so nothing interesting either. 713 name: "nil", 714 pErrGen: func(_ *roachpb.Transaction) *roachpb.Error { return nil }, 715 expEpoch: 0, 716 expPri: 1, 717 expTS: origTS, 718 expOrigTS: origTS, 719 }, 720 { 721 // On uncertainty error, new epoch begins and node is seen. 722 // Timestamp moves ahead of the existing write. 723 name: "ReadWithinUncertaintyIntervalError", 724 pErrGen: func(txn *roachpb.Transaction) *roachpb.Error { 725 const nodeID = 1 726 txn.UpdateObservedTimestamp(nodeID, plus10) 727 pErr := roachpb.NewErrorWithTxn( 728 roachpb.NewReadWithinUncertaintyIntervalError( 729 hlc.Timestamp{}, hlc.Timestamp{}, nil), 730 txn) 731 pErr.OriginNode = nodeID 732 return pErr 733 }, 734 expEpoch: 1, 735 expPri: 1, 736 expTS: plus10, 737 expOrigTS: plus10, 738 nodeSeen: true, 739 }, 740 { 741 // On abort, nothing changes but we get a new priority to use for 742 // the next attempt. 743 name: "TransactionAbortedError", 744 pErrGen: func(txn *roachpb.Transaction) *roachpb.Error { 745 txn.WriteTimestamp = plus20 746 txn.Priority = 10 747 return roachpb.NewErrorWithTxn(&roachpb.TransactionAbortedError{}, txn) 748 }, 749 expNewTransaction: true, 750 expPri: 10, 751 expTS: plus20, 752 expOrigTS: plus20, 753 }, 754 { 755 // On failed push, new epoch begins just past the pushed timestamp. 756 // Additionally, priority ratchets up to just below the pusher's. 757 name: "TransactionPushError", 758 pErrGen: func(txn *roachpb.Transaction) *roachpb.Error { 759 return roachpb.NewErrorWithTxn(&roachpb.TransactionPushError{ 760 PusheeTxn: roachpb.Transaction{ 761 TxnMeta: enginepb.TxnMeta{WriteTimestamp: plus10, Priority: 10}, 762 }, 763 }, txn) 764 }, 765 expEpoch: 1, 766 expPri: 9, 767 expTS: plus10, 768 expOrigTS: plus10, 769 }, 770 { 771 // On retry, restart with new epoch, timestamp and priority. 772 name: "TransactionRetryError", 773 pErrGen: func(txn *roachpb.Transaction) *roachpb.Error { 774 txn.WriteTimestamp = plus10 775 txn.Priority = 10 776 return roachpb.NewErrorWithTxn(&roachpb.TransactionRetryError{}, txn) 777 }, 778 expEpoch: 1, 779 expPri: 10, 780 expTS: plus10, 781 expOrigTS: plus10, 782 }, 783 } 784 785 for _, test := range testCases { 786 t.Run(test.name, func(t *testing.T) { 787 stopper := stop.NewStopper() 788 789 manual := hlc.NewManualClock(origTS.WallTime) 790 clock := hlc.NewClock(manual.UnixNano, 20*time.Nanosecond) 791 792 var senderFn kv.SenderFunc = func( 793 _ context.Context, ba roachpb.BatchRequest, 794 ) (*roachpb.BatchResponse, *roachpb.Error) { 795 var reply *roachpb.BatchResponse 796 pErr := test.pErrGen(ba.Txn) 797 if pErr == nil { 798 reply = ba.CreateReply() 799 reply.Txn = ba.Txn 800 } else if txn := pErr.GetTxn(); txn != nil { 801 // Update the manual clock to simulate an 802 // error updating a local hlc clock. 803 manual.Set(txn.WriteTimestamp.WallTime) 804 } 805 return reply, pErr 806 } 807 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 808 tsf := NewTxnCoordSenderFactory( 809 TxnCoordSenderFactoryConfig{ 810 AmbientCtx: ambient, 811 Clock: clock, 812 Stopper: stopper, 813 }, 814 senderFn, 815 ) 816 db := kv.NewDB(ambient, tsf, clock) 817 key := roachpb.Key("test-key") 818 now := clock.Now() 819 origTxnProto := roachpb.MakeTransaction( 820 "test txn", 821 key, 822 roachpb.UserPriority(0), 823 now, 824 clock.MaxOffset().Nanoseconds(), 825 ) 826 // TODO(andrei): I've monkeyed with the priorities on this initial 827 // Transaction to keep the test happy from a previous version in which the 828 // Transaction was not initialized before use (which became insufficient 829 // when we started testing that TransactionAbortedError's properly 830 // re-initializes the proto), but this deserves cleanup. I think this test 831 // is strict in what updated priorities it expects and also our mechanism 832 // for assigning exact priorities doesn't work properly when faced with 833 // updates. 834 origTxnProto.Priority = 1 835 txn := kv.NewTxnFromProto(ctx, db, 0 /* gatewayNodeID */, now, kv.RootTxn, &origTxnProto) 836 txn.TestingSetPriority(1) 837 838 err := txn.Put(ctx, key, []byte("value")) 839 stopper.Stop(ctx) 840 841 if test.name != "nil" && err == nil { 842 t.Fatalf("expected an error") 843 } 844 proto := txn.TestingCloneTxn() 845 txnReset := origTxnProto.ID != proto.ID 846 if txnReset != test.expNewTransaction { 847 t.Fatalf("expected txn reset: %t and got: %t", test.expNewTransaction, txnReset) 848 } 849 if proto.Epoch != test.expEpoch { 850 t.Errorf("expected epoch = %d; got %d", 851 test.expEpoch, proto.Epoch) 852 } 853 if proto.Priority != test.expPri { 854 t.Errorf("expected priority = %d; got %d", 855 test.expPri, proto.Priority) 856 } 857 if proto.WriteTimestamp != test.expTS { 858 t.Errorf("expected timestamp to be %s; got %s", 859 test.expTS, proto.WriteTimestamp) 860 } 861 if proto.ReadTimestamp != test.expOrigTS { 862 t.Errorf("expected orig timestamp to be %s; got %s", 863 test.expOrigTS, proto.ReadTimestamp) 864 } 865 if ns := proto.ObservedTimestamps; (len(ns) != 0) != test.nodeSeen { 866 t.Errorf("expected nodeSeen=%t, but list of hosts is %v", 867 test.nodeSeen, ns) 868 } 869 }) 870 } 871 } 872 873 // TestTxnMultipleCoord checks that multiple txn coordinators can be 874 // used for reads by a single transaction, and their state can be combined. 875 func TestTxnMultipleCoord(t *testing.T) { 876 defer leaktest.AfterTest(t)() 877 s := createTestDB(t) 878 defer s.Stop() 879 880 ctx := context.Background() 881 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 882 883 // Start the transaction. 884 key := roachpb.Key("a") 885 if _, err := txn.Get(ctx, key); err != nil { 886 t.Fatal(err) 887 } 888 889 // New create a second, leaf coordinator. 890 leafInputState := txn.GetLeafTxnInputState(ctx) 891 txn2 := kv.NewLeafTxn(ctx, s.DB, 0 /* gatewayNodeID */, &leafInputState) 892 893 // Start the second transaction. 894 key2 := roachpb.Key("b") 895 if _, err := txn2.Get(ctx, key2); err != nil { 896 t.Fatal(err) 897 } 898 899 // Augment txn with txn2's meta & commit. 900 tfs, err := txn2.GetLeafTxnFinalState(ctx) 901 if err != nil { 902 t.Fatal(err) 903 } 904 if err := txn.UpdateRootWithLeafFinalState(ctx, &tfs); err != nil { 905 t.Fatal(err) 906 } 907 908 // Verify presence of both locks. 909 tcs := txn.Sender().(*TxnCoordSender) 910 refreshSpans := tcs.interceptorAlloc.txnSpanRefresher.refreshFootprint.asSlice() 911 require.Equal(t, []roachpb.Span{{Key: key}, {Key: key2}}, refreshSpans) 912 913 ba := txn.NewBatch() 914 ba.AddRawRequest(&roachpb.EndTxnRequest{Commit: true}) 915 if err := txn.Run(ctx, ba); err != nil { 916 t.Fatal(err) 917 } 918 } 919 920 // TestTxnCoordSenderNoDuplicateLockSpans verifies that TxnCoordSender does not 921 // generate duplicate lock spans and that it merges lock spans that have 922 // overlapping ranges. 923 func TestTxnCoordSenderNoDuplicateLockSpans(t *testing.T) { 924 defer leaktest.AfterTest(t)() 925 ctx := context.Background() 926 stopper := stop.NewStopper() 927 manual := hlc.NewManualClock(123) 928 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 929 930 var expectedLockSpans []roachpb.Span 931 932 var senderFn kv.SenderFunc = func(_ context.Context, ba roachpb.BatchRequest) ( 933 *roachpb.BatchResponse, *roachpb.Error) { 934 br := ba.CreateReply() 935 br.Txn = ba.Txn.Clone() 936 if rArgs, ok := ba.GetArg(roachpb.EndTxn); ok { 937 et := rArgs.(*roachpb.EndTxnRequest) 938 if !reflect.DeepEqual(et.LockSpans, expectedLockSpans) { 939 t.Errorf("Invalid lock spans: %+v; expected %+v", et.LockSpans, expectedLockSpans) 940 } 941 br.Txn.Status = roachpb.COMMITTED 942 } 943 return br, nil 944 } 945 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 946 947 factory := NewTxnCoordSenderFactory( 948 TxnCoordSenderFactoryConfig{ 949 AmbientCtx: ambient, 950 Clock: clock, 951 Stopper: stopper, 952 Settings: cluster.MakeTestingClusterSettings(), 953 }, 954 senderFn, 955 ) 956 defer stopper.Stop(ctx) 957 958 db := kv.NewDB(ambient, factory, clock) 959 txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */) 960 961 // Acquire locks on a-b, c, u-w before the final batch. 962 _, pErr := txn.ReverseScanForUpdate(ctx, roachpb.Key("a"), roachpb.Key("b"), 0) 963 if pErr != nil { 964 t.Fatal(pErr) 965 } 966 pErr = txn.Put(ctx, roachpb.Key("c"), []byte("value")) 967 if pErr != nil { 968 t.Fatal(pErr) 969 } 970 pErr = txn.DelRange(ctx, roachpb.Key("u"), roachpb.Key("w")) 971 if pErr != nil { 972 t.Fatal(pErr) 973 } 974 975 // The final batch overwrites key c and overlaps part of the a-b and u-w ranges. 976 b := txn.NewBatch() 977 b.Put(roachpb.Key("b"), []byte("value")) 978 b.Put(roachpb.Key("c"), []byte("value")) 979 b.Put(roachpb.Key("d"), []byte("value")) 980 b.ReverseScanForUpdate(roachpb.Key("v"), roachpb.Key("z")) 981 982 // The expected locks are a-b, c, and u-z. 983 expectedLockSpans = []roachpb.Span{ 984 {Key: roachpb.Key("a"), EndKey: roachpb.Key("b").Next()}, 985 {Key: roachpb.Key("c"), EndKey: nil}, 986 {Key: roachpb.Key("d"), EndKey: nil}, 987 {Key: roachpb.Key("u"), EndKey: roachpb.Key("z")}, 988 } 989 990 pErr = txn.CommitInBatch(ctx, b) 991 if pErr != nil { 992 t.Fatal(pErr) 993 } 994 } 995 996 // checkTxnMetrics verifies that the provided Sender's transaction metrics match the expected 997 // values. This is done through a series of retries with increasing backoffs, to work around 998 // the TxnCoordSender's asynchronous updating of metrics after a transaction ends. 999 func checkTxnMetrics( 1000 t *testing.T, metrics TxnMetrics, name string, commits, commits1PC, aborts, restarts int64, 1001 ) { 1002 testutils.SucceedsSoon(t, func() error { 1003 return checkTxnMetricsOnce(t, metrics, name, commits, commits1PC, aborts, restarts) 1004 }) 1005 } 1006 1007 func checkTxnMetricsOnce( 1008 t *testing.T, metrics TxnMetrics, name string, commits, commits1PC, aborts, restarts int64, 1009 ) error { 1010 testcases := []struct { 1011 name string 1012 a, e int64 1013 }{ 1014 {"commits", metrics.Commits.Count(), commits}, 1015 {"commits1PC", metrics.Commits1PC.Count(), commits1PC}, 1016 {"aborts", metrics.Aborts.Count(), aborts}, 1017 {"durations", metrics.Durations.TotalCount(), commits + aborts}, 1018 } 1019 1020 for _, tc := range testcases { 1021 if tc.a != tc.e { 1022 return errors.Errorf("%s: actual %s %d != expected %d", name, tc.name, tc.a, tc.e) 1023 } 1024 } 1025 1026 // Handle restarts separately, because that's a histogram. Though the 1027 // histogram is approximate, we're recording so few distinct values 1028 // that we should be okay. 1029 dist := metrics.Restarts.Snapshot().Distribution() 1030 var actualRestarts int64 1031 for _, b := range dist { 1032 if b.From == b.To { 1033 actualRestarts += b.From * b.Count 1034 } else { 1035 t.Fatalf("unexpected value in histogram: %d-%d", b.From, b.To) 1036 } 1037 } 1038 if a, e := actualRestarts, restarts; a != e { 1039 return errors.Errorf("%s: actual restarts %d != expected %d", name, a, e) 1040 } 1041 1042 return nil 1043 } 1044 1045 // setupMetricsTest sets the txn coord sender factory's metrics to 1046 // have a faster sample interval and returns a cleanup function to be 1047 // executed by callers. 1048 func setupMetricsTest(t *testing.T) (*localtestcluster.LocalTestCluster, TxnMetrics, func()) { 1049 dbCtx := kv.DefaultDBContext() 1050 s := &localtestcluster.LocalTestCluster{ 1051 DBContext: &dbCtx, 1052 // Liveness heartbeat txns mess up the metrics. 1053 DisableLivenessHeartbeat: true, 1054 DontCreateSystemRanges: true, 1055 } 1056 s.Start(t, testutils.NewNodeTestBaseContext(), InitFactoryForLocalTestCluster) 1057 1058 metrics := MakeTxnMetrics(metric.TestSampleInterval) 1059 s.DB.GetFactory().(*TxnCoordSenderFactory).metrics = metrics 1060 return s, metrics, func() { 1061 s.Stop() 1062 } 1063 } 1064 1065 // Test a normal transaction. This and the other metrics tests below use real KV operations, 1066 // because it took far too much mucking with TxnCoordSender internals to mock out the sender 1067 // function as other tests do. 1068 func TestTxnCommit(t *testing.T) { 1069 defer leaktest.AfterTest(t)() 1070 s, metrics, cleanupFn := setupMetricsTest(t) 1071 defer cleanupFn() 1072 value := []byte("value") 1073 1074 // Test a write txn commit. 1075 if err := s.DB.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 1076 key := []byte("key-commit") 1077 return txn.Put(ctx, key, value) 1078 }); err != nil { 1079 t.Fatal(err) 1080 } 1081 checkTxnMetrics(t, metrics, "commit txn", 1 /* commits */, 0 /* commits1PC */, 0, 0) 1082 1083 // Test a read-only txn. 1084 if err := s.DB.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 1085 key := []byte("key-commit") 1086 _, err := txn.Get(ctx, key) 1087 return err 1088 }); err != nil { 1089 t.Fatal(err) 1090 } 1091 1092 checkTxnMetrics(t, metrics, "commit txn", 2 /* commits */, 0 /* commits1PC */, 0, 0) 1093 } 1094 1095 // TestTxnOnePhaseCommit verifies that 1PC metric tracking works. 1096 func TestTxnOnePhaseCommit(t *testing.T) { 1097 defer leaktest.AfterTest(t)() 1098 s, metrics, cleanupFn := setupMetricsTest(t) 1099 defer cleanupFn() 1100 1101 value := []byte("value") 1102 1103 ctx := context.Background() 1104 if err := s.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 1105 key := []byte("key-commit") 1106 b := txn.NewBatch() 1107 b.Put(key, value) 1108 return txn.CommitInBatch(ctx, b) 1109 }); err != nil { 1110 t.Fatal(err) 1111 } 1112 kv, err := s.DB.Get(ctx, []byte("key-commit")) 1113 if err != nil { 1114 t.Fatal(err) 1115 } 1116 if kv.Value == nil { 1117 t.Fatal("expected value not found") 1118 } 1119 val, err := kv.Value.GetBytes() 1120 if err != nil { 1121 t.Fatal(err) 1122 } 1123 if !bytes.Equal(val, value) { 1124 t.Fatalf("expected: %s, got: %s", value, val) 1125 } 1126 checkTxnMetrics(t, metrics, "commit 1PC txn", 1 /* commits */, 1 /* 1PC */, 0, 0) 1127 } 1128 1129 func TestTxnAbortCount(t *testing.T) { 1130 defer leaktest.AfterTest(t)() 1131 s, metrics, cleanupFn := setupMetricsTest(t) 1132 defer cleanupFn() 1133 1134 value := []byte("value") 1135 1136 const intentionalErrText = "intentional error to cause abort" 1137 // Test aborted transaction. 1138 if err := s.DB.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 1139 key := []byte("key-abort") 1140 1141 if err := txn.Put(ctx, key, value); err != nil { 1142 t.Fatal(err) 1143 } 1144 1145 return errors.New(intentionalErrText) 1146 }); !testutils.IsError(err, intentionalErrText) { 1147 t.Fatalf("unexpected error: %v", err) 1148 } 1149 checkTxnMetrics(t, metrics, "abort txn", 0, 0, 1 /* aborts */, 0) 1150 } 1151 1152 func TestTxnRestartCount(t *testing.T) { 1153 defer leaktest.AfterTest(t)() 1154 1155 readKey := []byte("read") 1156 writeKey := []byte("write") 1157 value := []byte("value") 1158 ctx := context.Background() 1159 1160 s, metrics, cleanupFn := setupMetricsTest(t) 1161 defer cleanupFn() 1162 1163 // Start a transaction and read a key that we're going to modify outside the 1164 // txn. This ensures that refreshing the txn will not succeed, so a restart 1165 // will be necessary. 1166 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 1167 if _, err := txn.Get(ctx, readKey); err != nil { 1168 t.Fatal(err) 1169 } 1170 1171 // Write the read key outside of the transaction, at a higher timestamp, which 1172 // will necessitate a txn restart when the original read key span is updated. 1173 if err := s.DB.Put(ctx, readKey, value); err != nil { 1174 t.Fatal(err) 1175 } 1176 1177 // Outside of the transaction, read the same key as will be 1178 // written within the transaction. This means that future 1179 // attempts to write will forward the txn timestamp. 1180 if _, err := s.DB.Get(ctx, writeKey); err != nil { 1181 t.Fatal(err) 1182 } 1183 1184 // This put will lay down an intent, txn timestamp will increase 1185 // beyond DeprecatedOrigTimestamp. 1186 if err := txn.Put(ctx, writeKey, value); err != nil { 1187 t.Fatal(err) 1188 } 1189 proto := txn.TestingCloneTxn() 1190 if proto.WriteTimestamp.LessEq(proto.ReadTimestamp) { 1191 t.Errorf("expected timestamp to increase: %s", proto) 1192 } 1193 1194 // Wait for heartbeat to start. 1195 tc := txn.Sender().(*TxnCoordSender) 1196 testutils.SucceedsSoon(t, func() error { 1197 if !tc.IsTracking() { 1198 return errors.New("expected heartbeat to start") 1199 } 1200 return nil 1201 }) 1202 1203 // Commit (should cause restart metric to increase). 1204 err := txn.CommitOrCleanup(ctx) 1205 assertTransactionRetryError(t, err) 1206 checkTxnMetrics(t, metrics, "restart txn", 0, 0, 1 /* aborts */, 1 /* restarts */) 1207 } 1208 1209 func TestTxnDurations(t *testing.T) { 1210 defer leaktest.AfterTest(t)() 1211 s, metrics, cleanupFn := setupMetricsTest(t) 1212 manual := s.Manual 1213 defer cleanupFn() 1214 const puts = 10 1215 1216 const incr int64 = 1000 1217 for i := 0; i < puts; i++ { 1218 key := roachpb.Key(fmt.Sprintf("key-txn-durations-%d", i)) 1219 if err := s.DB.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 1220 if err := txn.Put(ctx, key, []byte("val")); err != nil { 1221 return err 1222 } 1223 manual.Increment(incr) 1224 return nil 1225 }); err != nil { 1226 t.Fatal(err) 1227 } 1228 } 1229 1230 checkTxnMetrics(t, metrics, "txn durations", puts, 0, 0, 0) 1231 1232 hist := metrics.Durations 1233 // The clock is a bit odd in these tests, so I can't test the mean without 1234 // introducing spurious errors or being overly lax. 1235 // 1236 // TODO(cdo): look into cause of variance. 1237 if a, e := hist.TotalCount(), int64(puts); a != e { 1238 t.Fatalf("durations %d != expected %d", a, e) 1239 } 1240 1241 // Metrics lose fidelity, so we can't compare incr directly. 1242 if min, thresh := hist.Min(), incr-10; min < thresh { 1243 t.Fatalf("min %d < %d", min, thresh) 1244 } 1245 } 1246 1247 // TestAbortTransactionOnCommitErrors verifies that transactions are 1248 // aborted on the correct errors. 1249 func TestAbortTransactionOnCommitErrors(t *testing.T) { 1250 defer leaktest.AfterTest(t)() 1251 ctx := context.Background() 1252 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1253 1254 testCases := []struct { 1255 err error 1256 errFn func(roachpb.Transaction) *roachpb.Error 1257 asyncAbort bool 1258 }{ 1259 { 1260 errFn: func(txn roachpb.Transaction) *roachpb.Error { 1261 const nodeID = 0 1262 // ReadWithinUncertaintyIntervalErrors need a clock to have been 1263 // recorded on the origin. 1264 txn.UpdateObservedTimestamp(nodeID, makeTS(123, 0)) 1265 return roachpb.NewErrorWithTxn( 1266 roachpb.NewReadWithinUncertaintyIntervalError(hlc.Timestamp{}, hlc.Timestamp{}, nil), 1267 &txn) 1268 }, 1269 asyncAbort: false}, 1270 {err: &roachpb.TransactionAbortedError{}, asyncAbort: true}, 1271 {err: &roachpb.TransactionPushError{}, asyncAbort: false}, 1272 {err: &roachpb.TransactionRetryError{}, asyncAbort: false}, 1273 {err: &roachpb.RangeNotFoundError{}, asyncAbort: false}, 1274 {err: &roachpb.RangeKeyMismatchError{}, asyncAbort: false}, 1275 {err: &roachpb.TransactionStatusError{}, asyncAbort: false}, 1276 } 1277 1278 for _, test := range testCases { 1279 t.Run(fmt.Sprintf("%T", test.err), func(t *testing.T) { 1280 var commit, abort atomic.Value 1281 commit.Store(false) 1282 abort.Store(false) 1283 1284 stopper := stop.NewStopper() 1285 defer stopper.Stop(ctx) 1286 var senderFn kv.SenderFunc = func( 1287 _ context.Context, ba roachpb.BatchRequest, 1288 ) (*roachpb.BatchResponse, *roachpb.Error) { 1289 br := ba.CreateReply() 1290 br.Txn = ba.Txn.Clone() 1291 1292 if _, hasPut := ba.GetArg(roachpb.Put); hasPut { 1293 if _, ok := ba.Requests[0].GetInner().(*roachpb.PutRequest); !ok { 1294 t.Fatalf("expected Put") 1295 } 1296 union := &br.Responses[0] // avoid operating on copy 1297 union.MustSetInner(&roachpb.PutResponse{}) 1298 if ba.Txn != nil && br.Txn == nil { 1299 br.Txn.Status = roachpb.PENDING 1300 } 1301 } else if et, hasET := ba.GetArg(roachpb.EndTxn); hasET { 1302 if et.(*roachpb.EndTxnRequest).Commit { 1303 commit.Store(true) 1304 if test.errFn != nil { 1305 return nil, test.errFn(*ba.Txn) 1306 } 1307 return nil, roachpb.NewErrorWithTxn(test.err, ba.Txn) 1308 } 1309 abort.Store(true) 1310 } else { 1311 t.Fatalf("unexpected batch: %s", ba) 1312 } 1313 return br, nil 1314 } 1315 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1316 factory := NewTxnCoordSenderFactory( 1317 TxnCoordSenderFactoryConfig{ 1318 AmbientCtx: ambient, 1319 Clock: clock, 1320 Stopper: stopper, 1321 Settings: cluster.MakeTestingClusterSettings(), 1322 }, 1323 senderFn, 1324 ) 1325 1326 db := kv.NewDB(ambient, factory, clock) 1327 txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */) 1328 if pErr := txn.Put(ctx, "a", "b"); pErr != nil { 1329 t.Fatalf("put failed: %s", pErr) 1330 } 1331 if pErr := txn.CommitOrCleanup(ctx); pErr == nil { 1332 t.Fatalf("unexpected commit success") 1333 } 1334 1335 if !commit.Load().(bool) { 1336 t.Errorf("%T: failed to find initial commit request", test.err) 1337 } 1338 if !test.asyncAbort && !abort.Load().(bool) { 1339 t.Errorf("%T: failed to find expected synchronous abort", test.err) 1340 } else { 1341 testutils.SucceedsSoon(t, func() error { 1342 if !abort.Load().(bool) { 1343 return errors.Errorf("%T: failed to find expected asynchronous abort", test.err) 1344 } 1345 return nil 1346 }) 1347 } 1348 }) 1349 } 1350 } 1351 1352 // mockSender is a client.Sender implementation that passes requests to a list 1353 // of provided matchers, in sequence. The first matcher that returns either a 1354 // response or an error is used to provide the result for the request. 1355 type mockSender struct { 1356 matchers []matcher 1357 } 1358 1359 var _ kv.Sender = &mockSender{} 1360 1361 type matcher func(roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) 1362 1363 // match adds a matcher to the list of matchers. 1364 func (s *mockSender) match(m matcher) { 1365 s.matchers = append(s.matchers, m) 1366 } 1367 1368 // Send implements the client.Sender interface. 1369 func (s *mockSender) Send( 1370 _ context.Context, ba roachpb.BatchRequest, 1371 ) (*roachpb.BatchResponse, *roachpb.Error) { 1372 for _, m := range s.matchers { 1373 br, pErr := m(ba) 1374 if br != nil || pErr != nil { 1375 return br, pErr 1376 } 1377 } 1378 // If none of the matchers triggered, just create an empty reply. 1379 br := ba.CreateReply() 1380 br.Txn = ba.Txn.Clone() 1381 return br, nil 1382 } 1383 1384 // Test that a rollback sent to the TxnCoordSender stops the heartbeat loop even 1385 // if it encounters an error. As of June 2018, there's a separate code path for 1386 // handling errors on rollback in this regard. 1387 func TestRollbackErrorStopsHeartbeat(t *testing.T) { 1388 defer leaktest.AfterTest(t)() 1389 ctx := context.Background() 1390 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1391 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1392 sender := &mockSender{} 1393 stopper := stop.NewStopper() 1394 defer stopper.Stop(ctx) 1395 1396 factory := NewTxnCoordSenderFactory( 1397 TxnCoordSenderFactoryConfig{ 1398 AmbientCtx: log.AmbientContext{Tracer: tracing.NewTracer()}, 1399 Clock: clock, 1400 Stopper: stopper, 1401 Settings: cluster.MakeTestingClusterSettings(), 1402 }, 1403 sender, 1404 ) 1405 db := kv.NewDB(ambient, factory, clock) 1406 1407 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1408 if _, ok := ba.GetArg(roachpb.EndTxn); !ok { 1409 resp := ba.CreateReply() 1410 resp.Txn = ba.Txn 1411 return resp, nil 1412 } 1413 return nil, roachpb.NewErrorf("injected err") 1414 }) 1415 1416 txn := kv.NewTxn(ctx, db, roachpb.NodeID(1)) 1417 txnHeader := roachpb.Header{ 1418 Txn: txn.TestingCloneTxn(), 1419 } 1420 if _, pErr := kv.SendWrappedWith( 1421 ctx, txn, txnHeader, &roachpb.PutRequest{ 1422 RequestHeader: roachpb.RequestHeader{ 1423 Key: roachpb.Key("a"), 1424 }, 1425 }, 1426 ); pErr != nil { 1427 t.Fatal(pErr) 1428 } 1429 if !txn.Sender().(*TxnCoordSender).IsTracking() { 1430 t.Fatalf("expected TxnCoordSender to be tracking after the write") 1431 } 1432 1433 if _, pErr := kv.SendWrappedWith( 1434 ctx, txn, txnHeader, 1435 &roachpb.EndTxnRequest{Commit: false}, 1436 ); !testutils.IsPError(pErr, "injected err") { 1437 t.Fatal(pErr) 1438 } 1439 1440 testutils.SucceedsSoon(t, func() error { 1441 if txn.Sender().(*TxnCoordSender).IsTracking() { 1442 return fmt.Errorf("still tracking") 1443 } 1444 return nil 1445 }) 1446 } 1447 1448 // Test that lock tracking behaves correctly for transactions that attempt to 1449 // run a batch containing an EndTxn. Since in case of an error it's not easy to 1450 // determine whether any locks have been laid down (i.e. in case the batch was 1451 // split by the DistSender and then there was mixed success for the sub-batches, 1452 // or in case a retriable error is returned), the test verifies that all 1453 // possible locks are properly tracked and attached to a subsequent EndTxn. 1454 func TestOnePCErrorTracking(t *testing.T) { 1455 defer leaktest.AfterTest(t)() 1456 ctx := context.Background() 1457 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1458 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1459 sender := &mockSender{} 1460 stopper := stop.NewStopper() 1461 defer stopper.Stop(ctx) 1462 1463 factory := NewTxnCoordSenderFactory( 1464 TxnCoordSenderFactoryConfig{ 1465 AmbientCtx: log.AmbientContext{Tracer: tracing.NewTracer()}, 1466 Clock: clock, 1467 Stopper: stopper, 1468 Settings: cluster.MakeTestingClusterSettings(), 1469 }, 1470 sender, 1471 ) 1472 db := kv.NewDB(ambient, factory, clock) 1473 keyA, keyB, keyC := roachpb.Key("a"), roachpb.Key("b"), roachpb.Key("c") 1474 1475 // Register a matcher catching the commit attempt. 1476 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1477 if et, ok := ba.GetArg(roachpb.EndTxn); !ok { 1478 return nil, nil 1479 } else if !et.(*roachpb.EndTxnRequest).Commit { 1480 return nil, nil 1481 } 1482 return nil, roachpb.NewErrorf("injected err") 1483 }) 1484 // Register a matcher catching the rollback attempt. 1485 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1486 et, ok := ba.GetArg(roachpb.EndTxn) 1487 if !ok { 1488 return nil, nil 1489 } 1490 etReq := et.(*roachpb.EndTxnRequest) 1491 if etReq.Commit { 1492 return nil, nil 1493 } 1494 expLocks := []roachpb.Span{{Key: keyA}, {Key: keyB, EndKey: keyC}} 1495 locks := etReq.LockSpans 1496 if !reflect.DeepEqual(locks, expLocks) { 1497 return nil, roachpb.NewErrorf("expected locks %s, got: %s", expLocks, locks) 1498 } 1499 resp := ba.CreateReply() 1500 // Set the response's txn to the Aborted status (as the server would). This 1501 // will make the TxnCoordSender stop the heartbeat loop. 1502 resp.Txn = ba.Txn.Clone() 1503 resp.Txn.Status = roachpb.ABORTED 1504 return resp, nil 1505 }) 1506 1507 txn := kv.NewTxn(ctx, db, roachpb.NodeID(1)) 1508 txnHeader := roachpb.Header{ 1509 Txn: txn.TestingCloneTxn(), 1510 } 1511 b := txn.NewBatch() 1512 b.Put(keyA, "test value") 1513 b.ScanForUpdate(keyB, keyC) 1514 if err := txn.CommitInBatch(ctx, b); !testutils.IsError(err, "injected err") { 1515 t.Fatal(err) 1516 } 1517 1518 // Now send a rollback and verify that the TxnCoordSender attaches the locks 1519 // to it. 1520 if _, pErr := kv.SendWrappedWith( 1521 ctx, txn, txnHeader, 1522 &roachpb.EndTxnRequest{Commit: false}, 1523 ); pErr != nil { 1524 t.Fatal(pErr) 1525 } 1526 1527 // As always, check that the rollback we just sent stops the heartbeat loop. 1528 testutils.SucceedsSoon(t, func() error { 1529 if txn.Sender().(*TxnCoordSender).IsTracking() { 1530 return fmt.Errorf("still tracking") 1531 } 1532 return nil 1533 }) 1534 } 1535 1536 // TestCommitReadOnlyTransaction verifies that a read-only does not send an 1537 // EndTxnRequest. 1538 func TestCommitReadOnlyTransaction(t *testing.T) { 1539 defer leaktest.AfterTest(t)() 1540 ctx := context.Background() 1541 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1542 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1543 sender := &mockSender{} 1544 stopper := stop.NewStopper() 1545 defer stopper.Stop(ctx) 1546 1547 var calls []roachpb.Method 1548 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1549 calls = append(calls, ba.Methods()...) 1550 return nil, nil 1551 }) 1552 1553 factory := NewTxnCoordSenderFactory( 1554 TxnCoordSenderFactoryConfig{ 1555 AmbientCtx: ambient, 1556 Clock: clock, 1557 Stopper: stopper, 1558 Settings: cluster.MakeTestingClusterSettings(), 1559 }, 1560 sender, 1561 ) 1562 testutils.RunTrueAndFalse(t, "explicit txn", func(t *testing.T, explicitTxn bool) { 1563 testutils.RunTrueAndFalse(t, "with get", func(t *testing.T, withGet bool) { 1564 calls = nil 1565 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 1566 if err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 1567 b := txn.NewBatch() 1568 if withGet { 1569 b.Get("foo") 1570 } 1571 if explicitTxn { 1572 return txn.CommitInBatch(ctx, b) 1573 } 1574 return txn.Run(ctx, b) 1575 }); err != nil { 1576 t.Fatal(err) 1577 } 1578 1579 expectedCalls := []roachpb.Method(nil) 1580 if withGet { 1581 expectedCalls = append(expectedCalls, roachpb.Get) 1582 } 1583 if !reflect.DeepEqual(expectedCalls, calls) { 1584 t.Fatalf("expected %s, got %s", expectedCalls, calls) 1585 } 1586 }) 1587 }) 1588 } 1589 1590 // TestCommitMutatingTransaction verifies that a transaction is committed 1591 // upon successful invocation of the retryable func. 1592 func TestCommitMutatingTransaction(t *testing.T) { 1593 defer leaktest.AfterTest(t)() 1594 ctx := context.Background() 1595 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1596 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1597 sender := &mockSender{} 1598 stopper := stop.NewStopper() 1599 defer stopper.Stop(ctx) 1600 1601 var calls []roachpb.Method 1602 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1603 br := ba.CreateReply() 1604 br.Txn = ba.Txn.Clone() 1605 1606 calls = append(calls, ba.Methods()...) 1607 if !bytes.Equal(ba.Txn.Key, roachpb.Key("a")) { 1608 t.Errorf("expected transaction key to be \"a\"; got %s", ba.Txn.Key) 1609 } 1610 if et, ok := ba.GetArg(roachpb.EndTxn); ok { 1611 if !et.(*roachpb.EndTxnRequest).Commit { 1612 t.Errorf("expected commit to be true") 1613 } 1614 br.Txn.Status = roachpb.COMMITTED 1615 } 1616 return br, nil 1617 }) 1618 1619 factory := NewTxnCoordSenderFactory( 1620 TxnCoordSenderFactoryConfig{ 1621 AmbientCtx: ambient, 1622 Clock: clock, 1623 Stopper: stopper, 1624 Settings: cluster.MakeTestingClusterSettings(), 1625 }, 1626 sender, 1627 ) 1628 1629 // Test all transactional write methods. 1630 testArgs := []struct { 1631 f func(ctx context.Context, txn *kv.Txn) error 1632 expMethod roachpb.Method 1633 // pointWrite is set if the method is a "point write", which means that it 1634 // will be pipelined and we should expect a QueryIntent request at commit 1635 // time. 1636 pointWrite bool 1637 }{ 1638 { 1639 f: func(ctx context.Context, txn *kv.Txn) error { return txn.Put(ctx, "a", "b") }, 1640 expMethod: roachpb.Put, 1641 pointWrite: true, 1642 }, 1643 { 1644 f: func(ctx context.Context, txn *kv.Txn) error { return txn.CPut(ctx, "a", "b", nil) }, 1645 expMethod: roachpb.ConditionalPut, 1646 pointWrite: true, 1647 }, 1648 { 1649 f: func(ctx context.Context, txn *kv.Txn) error { 1650 _, err := txn.Inc(ctx, "a", 1) 1651 return err 1652 }, 1653 expMethod: roachpb.Increment, 1654 pointWrite: true, 1655 }, 1656 { 1657 f: func(ctx context.Context, txn *kv.Txn) error { return txn.Del(ctx, "a") }, 1658 expMethod: roachpb.Delete, 1659 pointWrite: true, 1660 }, 1661 { 1662 f: func(ctx context.Context, txn *kv.Txn) error { return txn.DelRange(ctx, "a", "b") }, 1663 expMethod: roachpb.DeleteRange, 1664 pointWrite: false, 1665 }, 1666 } 1667 for i, test := range testArgs { 1668 t.Run(test.expMethod.String(), func(t *testing.T) { 1669 calls = nil 1670 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 1671 if err := db.Txn(ctx, test.f); err != nil { 1672 t.Fatalf("%d: unexpected error on commit: %s", i, err) 1673 } 1674 expectedCalls := []roachpb.Method{test.expMethod} 1675 if test.pointWrite { 1676 expectedCalls = append(expectedCalls, roachpb.QueryIntent) 1677 } 1678 expectedCalls = append(expectedCalls, roachpb.EndTxn) 1679 if !reflect.DeepEqual(expectedCalls, calls) { 1680 t.Fatalf("%d: expected %s, got %s", i, expectedCalls, calls) 1681 } 1682 }) 1683 } 1684 } 1685 1686 // TestAbortReadOnlyTransaction verifies that aborting a read-only 1687 // transaction does not prompt an EndTxn call. 1688 func TestAbortReadOnlyTransaction(t *testing.T) { 1689 defer leaktest.AfterTest(t)() 1690 ctx := context.Background() 1691 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1692 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1693 sender := &mockSender{} 1694 stopper := stop.NewStopper() 1695 defer stopper.Stop(ctx) 1696 1697 var calls []roachpb.Method 1698 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1699 calls = append(calls, ba.Methods()...) 1700 return nil, nil 1701 }) 1702 1703 factory := NewTxnCoordSenderFactory( 1704 TxnCoordSenderFactoryConfig{ 1705 AmbientCtx: ambient, 1706 Clock: clock, 1707 Stopper: stopper, 1708 Settings: cluster.MakeTestingClusterSettings(), 1709 }, 1710 sender, 1711 ) 1712 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 1713 if err := db.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 1714 return errors.New("foo") 1715 }); err == nil { 1716 t.Fatal("expected error on abort") 1717 } 1718 1719 if calls != nil { 1720 t.Fatalf("expected no calls, got %s", calls) 1721 } 1722 } 1723 1724 // TestEndWriteRestartReadOnlyTransaction verifies that if 1725 // a transaction writes, then restarts and turns read-only, 1726 // an explicit EndTxn call is still sent if retry- able 1727 // didn't, regardless of whether there is an error or not. 1728 func TestEndWriteRestartReadOnlyTransaction(t *testing.T) { 1729 defer leaktest.AfterTest(t)() 1730 ctx := context.Background() 1731 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1732 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1733 sender := &mockSender{} 1734 stopper := stop.NewStopper() 1735 defer stopper.Stop(ctx) 1736 1737 var calls []roachpb.Method 1738 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1739 br := ba.CreateReply() 1740 br.Txn = ba.Txn.Clone() 1741 1742 calls = append(calls, ba.Methods()...) 1743 switch ba.Requests[0].GetInner().Method() { 1744 case roachpb.Put, roachpb.Scan: 1745 return nil, roachpb.NewErrorWithTxn( 1746 roachpb.NewTransactionRetryError(roachpb.RETRY_SERIALIZABLE, "test err"), 1747 ba.Txn) 1748 case roachpb.EndTxn: 1749 br.Txn.Status = roachpb.COMMITTED 1750 } 1751 return br, nil 1752 }) 1753 1754 factory := NewTxnCoordSenderFactory( 1755 TxnCoordSenderFactoryConfig{ 1756 AmbientCtx: ambient, 1757 Clock: clock, 1758 Stopper: stopper, 1759 Settings: cluster.MakeTestingClusterSettings(), 1760 TestingKnobs: ClientTestingKnobs{ 1761 // Disable span refresh, otherwise it kicks and retries batches by 1762 // itself. 1763 MaxTxnRefreshAttempts: -1, 1764 }, 1765 }, 1766 sender, 1767 ) 1768 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 1769 1770 testutils.RunTrueAndFalse(t, "write", func(t *testing.T, write bool) { 1771 testutils.RunTrueAndFalse(t, "success", func(t *testing.T, success bool) { 1772 calls = nil 1773 firstIter := true 1774 if err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 1775 if firstIter { 1776 firstIter = false 1777 var err error 1778 if write { 1779 err = txn.Put(ctx, "consider", "phlebas") 1780 } else /* locking read */ { 1781 _, err = txn.ScanForUpdate(ctx, "a", "b", 0) 1782 } 1783 if err == nil { 1784 t.Fatal("missing injected retriable error") 1785 } 1786 } 1787 if !success { 1788 return errors.New("aborting on purpose") 1789 } 1790 return nil 1791 }); err == nil != success { 1792 t.Fatalf("expected error: %t, got error: %v", !success, err) 1793 } 1794 1795 var expCalls []roachpb.Method 1796 if write { 1797 expCalls = []roachpb.Method{roachpb.Put, roachpb.EndTxn} 1798 } else { 1799 expCalls = []roachpb.Method{roachpb.Scan, roachpb.EndTxn} 1800 } 1801 if !reflect.DeepEqual(expCalls, calls) { 1802 t.Fatalf("expected %v, got %v", expCalls, calls) 1803 } 1804 }) 1805 }) 1806 } 1807 1808 // TestTransactionKeyNotChangedInRestart verifies that if the transaction 1809 // already has a key (we're in a restart), the key in the transaction request is 1810 // not changed. 1811 func TestTransactionKeyNotChangedInRestart(t *testing.T) { 1812 defer leaktest.AfterTest(t)() 1813 ctx := context.Background() 1814 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1815 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1816 sender := &mockSender{} 1817 stopper := stop.NewStopper() 1818 defer stopper.Stop(ctx) 1819 1820 keys := []string{"first", "second"} 1821 attempt := 0 1822 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1823 br := ba.CreateReply() 1824 br.Txn = ba.Txn.Clone() 1825 1826 // Ignore the final EndTxnRequest. 1827 if _, ok := ba.GetArg(roachpb.EndTxn); ok { 1828 br.Txn.Status = roachpb.COMMITTED 1829 return br, nil 1830 } 1831 1832 // Both attempts should have a PutRequest. 1833 if _, ok := ba.GetArg(roachpb.Put); !ok { 1834 t.Fatalf("failed to find a put request: %v", ba) 1835 } 1836 1837 // In the first attempt, the transaction key is the key of the first write command. 1838 // This key is retained between restarts, so we see the same key in the second attempt. 1839 if expectedKey := []byte(keys[0]); !bytes.Equal(expectedKey, ba.Txn.Key) { 1840 t.Fatalf("expected transaction key %v, got %v", expectedKey, ba.Txn.Key) 1841 } 1842 1843 if attempt == 0 { 1844 return nil, roachpb.NewErrorWithTxn( 1845 roachpb.NewTransactionRetryError(roachpb.RETRY_SERIALIZABLE, "test err"), 1846 ba.Txn) 1847 } 1848 return br, nil 1849 }) 1850 factory := NewTxnCoordSenderFactory( 1851 TxnCoordSenderFactoryConfig{ 1852 AmbientCtx: ambient, 1853 Clock: clock, 1854 Stopper: stopper, 1855 Settings: cluster.MakeTestingClusterSettings(), 1856 }, 1857 sender, 1858 ) 1859 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 1860 1861 if err := db.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 1862 defer func() { attempt++ }() 1863 b := txn.NewBatch() 1864 b.Put(keys[attempt], "b") 1865 return txn.Run(ctx, b) 1866 }); err != nil { 1867 t.Errorf("unexpected error on commit: %s", err) 1868 } 1869 minimumAttempts := 2 1870 if attempt < minimumAttempts { 1871 t.Errorf("expected attempt count >= %d, got %d", minimumAttempts, attempt) 1872 } 1873 } 1874 1875 // TestSequenceNumbers verifies Requests are given sequence numbers and that 1876 // they are incremented on successive commands. 1877 func TestSequenceNumbers(t *testing.T) { 1878 defer leaktest.AfterTest(t)() 1879 ctx := context.Background() 1880 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1881 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1882 sender := &mockSender{} 1883 stopper := stop.NewStopper() 1884 defer stopper.Stop(ctx) 1885 1886 var expSequence enginepb.TxnSeq 1887 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1888 for _, ru := range ba.Requests { 1889 args := ru.GetInner() 1890 if args.Method() == roachpb.QueryIntent { 1891 // QueryIntent requests don't have sequence numbers. 1892 continue 1893 } 1894 expSequence++ 1895 if seq := args.Header().Sequence; expSequence != seq { 1896 t.Errorf("expected Request sequence %d; got %d. request: %T", 1897 expSequence, seq, args) 1898 } 1899 } 1900 br := ba.CreateReply() 1901 br.Txn = ba.Txn 1902 return br, nil 1903 }) 1904 1905 factory := NewTxnCoordSenderFactory( 1906 TxnCoordSenderFactoryConfig{ 1907 AmbientCtx: ambient, 1908 Clock: clock, 1909 Stopper: stopper, 1910 Settings: cluster.MakeTestingClusterSettings(), 1911 }, 1912 sender, 1913 ) 1914 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 1915 txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */) 1916 1917 for i := 0; i < 5; i++ { 1918 var ba roachpb.BatchRequest 1919 for j := 0; j < i; j++ { 1920 ba.Add(roachpb.NewPut(roachpb.Key("a"), roachpb.MakeValueFromString("foo")).(*roachpb.PutRequest)) 1921 } 1922 if _, pErr := txn.Send(ctx, ba); pErr != nil { 1923 t.Fatal(pErr) 1924 } 1925 } 1926 } 1927 1928 // TestConcurrentTxnRequests verifies that multiple requests cannot be executed 1929 // on a transaction at the same time from multiple goroutines. 1930 func TestConcurrentTxnRequestsProhibited(t *testing.T) { 1931 defer leaktest.AfterTest(t)() 1932 ctx := context.Background() 1933 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1934 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1935 sender := &mockSender{} 1936 stopper := stop.NewStopper() 1937 defer stopper.Stop(ctx) 1938 1939 putSync := make(chan struct{}) 1940 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 1941 if _, ok := ba.GetArg(roachpb.Put); ok { 1942 // Block the Put until the Get runs. 1943 putSync <- struct{}{} 1944 <-putSync 1945 } 1946 br := ba.CreateReply() 1947 br.Txn = ba.Txn.Clone() 1948 if _, ok := ba.GetArg(roachpb.EndTxn); ok { 1949 br.Txn.Status = roachpb.COMMITTED 1950 } 1951 return br, nil 1952 }) 1953 1954 factory := NewTxnCoordSenderFactory( 1955 TxnCoordSenderFactoryConfig{ 1956 AmbientCtx: ambient, 1957 Clock: clock, 1958 Stopper: stopper, 1959 Settings: cluster.MakeTestingClusterSettings(), 1960 }, 1961 sender, 1962 ) 1963 db := kv.NewDB(ambient, factory, clock) 1964 1965 err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 1966 g, gCtx := errgroup.WithContext(ctx) 1967 g.Go(func() error { 1968 return txn.Put(gCtx, "test_put", "val") 1969 }) 1970 g.Go(func() error { 1971 // Wait for the Put to be blocked. 1972 <-putSync 1973 _, err := txn.Get(gCtx, "test_get") 1974 // Unblock the Put. 1975 putSync <- struct{}{} 1976 return err 1977 }) 1978 return g.Wait() 1979 }) 1980 require.Regexp(t, "concurrent txn use detected", err) 1981 } 1982 1983 // TestTxnRequestTxnTimestamp verifies response txn timestamp is 1984 // always upgraded on successive requests. 1985 func TestTxnRequestTxnTimestamp(t *testing.T) { 1986 defer leaktest.AfterTest(t)() 1987 ctx := context.Background() 1988 manual := hlc.NewManualClock(123) 1989 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 1990 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 1991 sender := &mockSender{} 1992 stopper := stop.NewStopper() 1993 defer stopper.Stop(ctx) 1994 1995 factory := NewTxnCoordSenderFactory( 1996 TxnCoordSenderFactoryConfig{ 1997 AmbientCtx: ambient, 1998 Clock: clock, 1999 Stopper: stopper, 2000 Settings: cluster.MakeTestingClusterSettings(), 2001 }, 2002 sender, 2003 ) 2004 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 2005 2006 curReq := 0 2007 requests := []struct { 2008 expRequestTS, responseTS hlc.Timestamp 2009 }{ 2010 {hlc.Timestamp{WallTime: 5, Logical: 0}, hlc.Timestamp{WallTime: 10, Logical: 0}}, 2011 {hlc.Timestamp{WallTime: 10, Logical: 0}, hlc.Timestamp{WallTime: 10, Logical: 1}}, 2012 {hlc.Timestamp{WallTime: 10, Logical: 1}, hlc.Timestamp{WallTime: 10, Logical: 0}}, 2013 {hlc.Timestamp{WallTime: 10, Logical: 1}, hlc.Timestamp{WallTime: 20, Logical: 1}}, 2014 {hlc.Timestamp{WallTime: 20, Logical: 1}, hlc.Timestamp{WallTime: 20, Logical: 1}}, 2015 {hlc.Timestamp{WallTime: 20, Logical: 1}, hlc.Timestamp{WallTime: 19, Logical: 0}}, 2016 {hlc.Timestamp{WallTime: 20, Logical: 1}, hlc.Timestamp{WallTime: 20, Logical: 1}}, 2017 } 2018 2019 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 2020 req := requests[curReq] 2021 if req.expRequestTS != ba.Txn.WriteTimestamp { 2022 return nil, roachpb.NewErrorf("%d: expected ts %s got %s", 2023 curReq, req.expRequestTS, ba.Txn.WriteTimestamp) 2024 } 2025 2026 br := ba.CreateReply() 2027 br.Txn = ba.Txn.Clone() 2028 br.Txn.WriteTimestamp.Forward(requests[curReq].responseTS) 2029 return br, nil 2030 }) 2031 2032 manual.Set(requests[0].expRequestTS.WallTime) 2033 2034 if err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 2035 for curReq = range requests { 2036 if _, err := txn.Get(ctx, "k"); err != nil { 2037 return err 2038 } 2039 } 2040 return nil 2041 }); err != nil { 2042 t.Fatal(err) 2043 } 2044 } 2045 2046 // TestReadOnlyTxnObeysDeadline tests that read-only transactions obey the 2047 // deadline. Read-only transactions have their EndTxn elided, so the enforcement 2048 // of the deadline is done in the client. 2049 func TestReadOnlyTxnObeysDeadline(t *testing.T) { 2050 defer leaktest.AfterTest(t)() 2051 ctx := context.Background() 2052 manual := hlc.NewManualClock(123) 2053 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 2054 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 2055 sender := &mockSender{} 2056 stopper := stop.NewStopper() 2057 defer stopper.Stop(ctx) 2058 2059 sender.match(func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { 2060 if _, ok := ba.GetArg(roachpb.Get); ok { 2061 manual.Increment(100) 2062 br := ba.CreateReply() 2063 br.Txn = ba.Txn.Clone() 2064 br.Txn.WriteTimestamp.Forward(clock.Now()) 2065 return br, nil 2066 } 2067 return nil, nil 2068 }) 2069 2070 factory := NewTxnCoordSenderFactory( 2071 TxnCoordSenderFactoryConfig{ 2072 AmbientCtx: ambient, 2073 Clock: clock, 2074 Stopper: stopper, 2075 Settings: cluster.MakeTestingClusterSettings(), 2076 }, 2077 sender, 2078 ) 2079 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 2080 2081 // We're going to run two tests: one where the EndTxn is by itself in a 2082 // batch, one where it is not. As of June 2018, the EndTxn is elided in 2083 // different ways in the two cases. 2084 2085 t.Run("standalone commit", func(t *testing.T) { 2086 txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */) 2087 // Set a deadline. We'll generate a retriable error with a higher timestamp. 2088 txn.UpdateDeadlineMaybe(ctx, clock.Now()) 2089 if _, err := txn.Get(ctx, "k"); err != nil { 2090 t.Fatal(err) 2091 } 2092 err := txn.Commit(ctx) 2093 assertTransactionRetryError(t, err) 2094 if !testutils.IsError(err, "RETRY_COMMIT_DEADLINE_EXCEEDED") { 2095 t.Fatalf("expected deadline exceeded, got: %s", err) 2096 } 2097 }) 2098 2099 t.Run("commit in batch", func(t *testing.T) { 2100 txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */) 2101 // Set a deadline. We'll generate a retriable error with a higher timestamp. 2102 txn.UpdateDeadlineMaybe(ctx, clock.Now()) 2103 b := txn.NewBatch() 2104 b.Get("k") 2105 err := txn.CommitInBatch(ctx, b) 2106 assertTransactionRetryError(t, err) 2107 if !testutils.IsError(err, "RETRY_COMMIT_DEADLINE_EXCEEDED") { 2108 t.Fatalf("expected deadline exceeded, got: %s", err) 2109 } 2110 }) 2111 } 2112 2113 // TestTxnCoordSenderPipelining verifies that transactional pipelining of writes 2114 // is enabled by default in a transaction and is disabled after 2115 // DisablePipelining is called. It also verifies that DisablePipelining returns 2116 // an error if the transaction has already performed an operation. 2117 func TestTxnCoordSenderPipelining(t *testing.T) { 2118 defer leaktest.AfterTest(t)() 2119 2120 ctx := context.Background() 2121 s := createTestDB(t) 2122 defer s.Stop() 2123 distSender := s.DB.GetFactory().(*TxnCoordSenderFactory).NonTransactionalSender() 2124 2125 var calls []roachpb.Method 2126 var senderFn kv.SenderFunc = func( 2127 ctx context.Context, ba roachpb.BatchRequest, 2128 ) (*roachpb.BatchResponse, *roachpb.Error) { 2129 calls = append(calls, ba.Methods()...) 2130 if et, ok := ba.GetArg(roachpb.EndTxn); ok { 2131 // Ensure that no transactions enter a STAGING state. 2132 et.(*roachpb.EndTxnRequest).InFlightWrites = nil 2133 } 2134 return distSender.Send(ctx, ba) 2135 } 2136 2137 ambientCtx := log.AmbientContext{Tracer: tracing.NewTracer()} 2138 tsf := NewTxnCoordSenderFactory(TxnCoordSenderFactoryConfig{ 2139 AmbientCtx: ambientCtx, 2140 Settings: s.Cfg.Settings, 2141 Clock: s.Clock, 2142 Stopper: s.Stopper, 2143 }, senderFn) 2144 db := kv.NewDB(ambientCtx, tsf, s.Clock) 2145 2146 err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 2147 return txn.Put(ctx, "key", "val") 2148 }) 2149 if err != nil { 2150 t.Fatal(err) 2151 } 2152 2153 err = db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 2154 if err := txn.DisablePipelining(); err != nil { 2155 return err 2156 } 2157 return txn.Put(ctx, "key", "val") 2158 }) 2159 if err != nil { 2160 t.Fatal(err) 2161 } 2162 2163 require.Equal(t, []roachpb.Method{ 2164 roachpb.Put, roachpb.QueryIntent, roachpb.EndTxn, 2165 roachpb.Put, roachpb.EndTxn, 2166 }, calls) 2167 2168 for _, action := range []func(ctx context.Context, txn *kv.Txn) error{ 2169 func(ctx context.Context, txn *kv.Txn) error { return txn.Put(ctx, "key", "val") }, 2170 func(ctx context.Context, txn *kv.Txn) error { _, err := txn.Get(ctx, "key"); return err }, 2171 } { 2172 err = db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 2173 if err := action(ctx, txn); err != nil { 2174 t.Fatal(err) 2175 } 2176 return txn.DisablePipelining() 2177 }) 2178 if exp := "cannot disable pipelining on a running transaction"; !testutils.IsError(err, exp) { 2179 t.Fatalf("expected %q error, but got %v", exp, err) 2180 } 2181 } 2182 } 2183 2184 // Test that a txn's anchor is set to the first write key in batches mixing 2185 // reads with writes. 2186 func TestAnchorKey(t *testing.T) { 2187 defer leaktest.AfterTest(t)() 2188 2189 ctx := context.Background() 2190 manual := hlc.NewManualClock(123) 2191 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 2192 ambient := log.AmbientContext{Tracer: tracing.NewTracer()} 2193 stopper := stop.NewStopper() 2194 defer stopper.Stop(ctx) 2195 2196 key1 := roachpb.Key("a") 2197 key2 := roachpb.Key("b") 2198 2199 var senderFn kv.SenderFunc = func( 2200 ctx context.Context, ba roachpb.BatchRequest, 2201 ) (*roachpb.BatchResponse, *roachpb.Error) { 2202 if !roachpb.Key(ba.Txn.Key).Equal(key2) { 2203 t.Fatalf("expected anchor %q, got %q", key2, ba.Txn.Key) 2204 } 2205 br := ba.CreateReply() 2206 br.Txn = ba.Txn.Clone() 2207 if _, ok := ba.GetArg(roachpb.EndTxn); ok { 2208 br.Txn.Status = roachpb.COMMITTED 2209 } 2210 return br, nil 2211 } 2212 2213 factory := NewTxnCoordSenderFactory( 2214 TxnCoordSenderFactoryConfig{ 2215 AmbientCtx: ambient, 2216 Clock: clock, 2217 Stopper: stopper, 2218 Settings: cluster.MakeTestingClusterSettings(), 2219 }, 2220 senderFn, 2221 ) 2222 db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock) 2223 2224 if err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 2225 ba := txn.NewBatch() 2226 ba.Get(key1) 2227 ba.Put(key2, "val") 2228 return txn.Run(ctx, ba) 2229 }); err != nil { 2230 t.Fatal(err) 2231 } 2232 } 2233 2234 // Test that a leaf txn returns a raw error when "rejecting a client" (a client 2235 // sending something after the txn is known to be aborted), not a 2236 // TransactionRetryWithProtoRefreshError. This is important as leaves are not supposed to create 2237 // "handled" errors; instead the DistSQL infra knows to recognize raw retryable 2238 // errors and feed them to the root txn. 2239 func TestLeafTxnClientRejectError(t *testing.T) { 2240 defer leaktest.AfterTest(t)() 2241 2242 // We're going to inject an error so that a leaf txn is "poisoned". This can 2243 // happen, for example, if the leaf is used concurrently by multiple requests, 2244 // where the first one gets a TransactionAbortedError. 2245 errKey := roachpb.Key("a") 2246 knobs := &kvserver.StoreTestingKnobs{ 2247 TestingRequestFilter: func(_ context.Context, ba roachpb.BatchRequest) *roachpb.Error { 2248 if g, ok := ba.GetArg(roachpb.Get); ok && g.(*roachpb.GetRequest).Key.Equal(errKey) { 2249 txn := ba.Txn.Clone() 2250 txn.Status = roachpb.ABORTED 2251 return roachpb.NewErrorWithTxn( 2252 roachpb.NewTransactionAbortedError(roachpb.ABORT_REASON_UNKNOWN), txn, 2253 ) 2254 } 2255 return nil 2256 }, 2257 } 2258 2259 s := createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), knobs) 2260 defer s.Stop() 2261 2262 ctx := context.Background() 2263 rootTxn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 2264 leafInputState := rootTxn.GetLeafTxnInputState(ctx) 2265 2266 // New create a second, leaf coordinator. 2267 leafTxn := kv.NewLeafTxn(ctx, s.DB, 0 /* gatewayNodeID */, &leafInputState) 2268 2269 if _, err := leafTxn.Get(ctx, errKey); !testutils.IsError(err, "TransactionAbortedError") { 2270 t.Fatalf("expected injected err, got: %v", err) 2271 } 2272 2273 // Now use the leaf and check the error. At the TxnCoordSender level, the 2274 // pErr will be TransactionAbortedError. When pErr.GoError() is called, that's 2275 // transformed into an UnhandledRetryableError. For our purposes, what this 2276 // test is interested in demonstrating is that it's not a 2277 // TransactionRetryWithProtoRefreshError. 2278 _, err := leafTxn.Get(ctx, roachpb.Key("a")) 2279 if !errors.HasType(err, (*roachpb.UnhandledRetryableError)(nil)) { 2280 t.Fatalf("expected UnhandledRetryableError(TransactionAbortedError), got: (%T) %v", err, err) 2281 } 2282 } 2283 2284 // Check that ingesting an Aborted txn record is a no-op. The TxnCoordSender is 2285 // supposed to reject such updates because they risk putting it into an 2286 // inconsistent state. See comments in TxnCoordSender.UpdateRootWithLeafFinalState(). 2287 func TestUpdateRoootWithLeafFinalStateInAbortedTxn(t *testing.T) { 2288 defer leaktest.AfterTest(t)() 2289 s := createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), nil /* knobs */) 2290 defer s.Stop() 2291 ctx := context.Background() 2292 2293 txn := kv.NewTxn(ctx, s.DB, 0 /* gatewayNodeID */) 2294 leafInputState := txn.GetLeafTxnInputState(ctx) 2295 leafTxn := kv.NewLeafTxn(ctx, s.DB, 0, &leafInputState) 2296 2297 finalState, err := leafTxn.GetLeafTxnFinalState(ctx) 2298 if err != nil { 2299 t.Fatal(err) 2300 } 2301 finalState.Txn.Status = roachpb.ABORTED 2302 if err := txn.UpdateRootWithLeafFinalState(ctx, &finalState); err != nil { 2303 t.Fatal(err) 2304 } 2305 2306 // Check that the transaction was not updated. 2307 leafInputState2 := txn.GetLeafTxnInputState(ctx) 2308 if leafInputState2.Txn.Status != roachpb.PENDING { 2309 t.Fatalf("expected PENDING txn, got: %s", leafInputState2.Txn.Status) 2310 } 2311 }