github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/transaction_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package batcheval 12 13 import ( 14 "context" 15 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/abortspan" 18 "github.com/cockroachdb/cockroach/pkg/roachpb" 19 "github.com/cockroachdb/cockroach/pkg/storage" 20 "github.com/cockroachdb/cockroach/pkg/util/hlc" 21 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 22 "github.com/stretchr/testify/require" 23 ) 24 25 // TestUpdateAbortSpan tests the different ways that request can set, update, 26 // and delete AbortSpan entries. 27 func TestUpdateAbortSpan(t *testing.T) { 28 defer leaktest.AfterTest(t)() 29 30 ctx := context.Background() 31 startKey := roachpb.Key("0000") 32 txnKey := roachpb.Key("1111") 33 intentKey := roachpb.Key("2222") 34 endKey := roachpb.Key("9999") 35 desc := roachpb.RangeDescriptor{ 36 RangeID: 99, 37 StartKey: roachpb.RKey(startKey), 38 EndKey: roachpb.RKey(endKey), 39 } 40 as := abortspan.New(desc.RangeID) 41 42 txn := roachpb.MakeTransaction("test", txnKey, 0, hlc.Timestamp{WallTime: 1}, 0) 43 newTxnAbortSpanEntry := roachpb.AbortSpanEntry{ 44 Key: txn.Key, 45 Timestamp: txn.WriteTimestamp, 46 Priority: txn.Priority, 47 } 48 // Used to detect updates to the AbortSpan entry. WriteTimestamp and 49 // Priority don't matter other than that they allow us to detect changes 50 // in the AbortSpanEntry. 51 prevTxn := txn.Clone() 52 prevTxn.WriteTimestamp.Add(-1, 0) 53 prevTxn.Priority-- 54 prevTxnAbortSpanEntry := roachpb.AbortSpanEntry{ 55 Key: prevTxn.Key, 56 Timestamp: prevTxn.WriteTimestamp, 57 Priority: prevTxn.Priority, 58 } 59 60 // Setup helpers. 61 type evalFn func(storage.ReadWriter, EvalContext) error 62 addIntent := func(b storage.ReadWriter, _ EvalContext) error { 63 val := roachpb.MakeValueFromString("val") 64 return storage.MVCCPut(ctx, b, nil /* ms */, intentKey, txn.ReadTimestamp, val, &txn) 65 } 66 addPrevAbortSpanEntry := func(b storage.ReadWriter, rec EvalContext) error { 67 return UpdateAbortSpan(ctx, rec, b, nil /* ms */, prevTxn.TxnMeta, true /* poison */) 68 } 69 compose := func(fns ...evalFn) evalFn { 70 return func(b storage.ReadWriter, rec EvalContext) error { 71 for _, fn := range fns { 72 if err := fn(b, rec); err != nil { 73 return err 74 } 75 } 76 return nil 77 } 78 } 79 80 // Request helpers. 81 endTxn := func(b storage.ReadWriter, rec EvalContext, commit bool, poison bool) error { 82 req := roachpb.EndTxnRequest{ 83 RequestHeader: roachpb.RequestHeader{Key: txnKey}, 84 Commit: commit, 85 Poison: poison, 86 LockSpans: []roachpb.Span{{Key: intentKey}}, 87 } 88 args := CommandArgs{ 89 EvalCtx: rec, 90 Header: roachpb.Header{ 91 Timestamp: txn.ReadTimestamp, 92 Txn: &txn, 93 }, 94 Args: &req, 95 } 96 97 var resp roachpb.EndTxnResponse 98 _, err := EndTxn(ctx, b, args, &resp) 99 return err 100 } 101 resolveIntent := func( 102 b storage.ReadWriter, rec EvalContext, status roachpb.TransactionStatus, poison bool, 103 ) error { 104 req := roachpb.ResolveIntentRequest{ 105 RequestHeader: roachpb.RequestHeader{Key: intentKey}, 106 IntentTxn: txn.TxnMeta, 107 Status: status, 108 Poison: poison, 109 } 110 args := CommandArgs{ 111 EvalCtx: rec, 112 Args: &req, 113 } 114 115 var resp roachpb.ResolveIntentResponse 116 _, err := ResolveIntent(ctx, b, args, &resp) 117 return err 118 } 119 resolveIntentRange := func( 120 b storage.ReadWriter, rec EvalContext, status roachpb.TransactionStatus, poison bool, 121 ) error { 122 req := roachpb.ResolveIntentRangeRequest{ 123 RequestHeader: roachpb.RequestHeader{Key: startKey, EndKey: endKey}, 124 IntentTxn: txn.TxnMeta, 125 Status: status, 126 Poison: poison, 127 } 128 args := CommandArgs{ 129 EvalCtx: rec, 130 Args: &req, 131 } 132 133 var resp roachpb.ResolveIntentRangeResponse 134 _, err := ResolveIntentRange(ctx, b, args, &resp) 135 return err 136 } 137 138 testCases := []struct { 139 name string 140 before evalFn 141 run evalFn // nil if invalid test case 142 exp *roachpb.AbortSpanEntry // nil if no entry expected 143 expErr string // empty if no error expected 144 }{ 145 /////////////////////////////////////////////////////////////////////// 146 // EndTxnRequest // 147 /////////////////////////////////////////////////////////////////////// 148 { 149 name: "end txn, rollback, no poison, intent missing, abort span missing", 150 run: func(b storage.ReadWriter, rec EvalContext) error { 151 return endTxn(b, rec, false /* commit */, false /* poison */) 152 }, 153 // Not poisoning, should not add an abort span entry. 154 exp: nil, 155 }, 156 { 157 name: "end txn, rollback, no poison, intent missing, abort span present", 158 before: addPrevAbortSpanEntry, 159 run: func(b storage.ReadWriter, rec EvalContext) error { 160 return endTxn(b, rec, false /* commit */, false /* poison */) 161 }, 162 // Not poisoning, should clean up abort span entry. 163 exp: nil, 164 }, 165 { 166 name: "end txn, rollback, poison, intent missing, abort span missing", 167 run: func(b storage.ReadWriter, rec EvalContext) error { 168 return endTxn(b, rec, false /* commit */, true /* poison */) 169 }, 170 // Poisoning, but no intents found, should not add an abort span entry. 171 exp: nil, 172 }, 173 { 174 name: "end txn, rollback, poison, intent missing, abort span present", 175 before: addPrevAbortSpanEntry, 176 run: func(b storage.ReadWriter, rec EvalContext) error { 177 return endTxn(b, rec, false /* commit */, true /* poison */) 178 }, 179 // Poisoning, but no intents found, don't touch abort span. 180 exp: &prevTxnAbortSpanEntry, 181 }, 182 { 183 name: "end txn, commit, no poison, intent missing, abort span missing", 184 run: func(b storage.ReadWriter, rec EvalContext) error { 185 return endTxn(b, rec, true /* commit */, false /* poison */) 186 }, 187 // Not poisoning, should not add an abort span entry. 188 exp: nil, 189 }, 190 { 191 // NOTE: this request doesn't make sense. An abort span shouldn't be 192 // present if the transaction is still committable. 193 name: "end txn, commit, no poison, intent missing, abort span present", 194 }, 195 { 196 // It is an error for EndTxn to pass Commit = true and Poison = true. 197 name: "end txn, commit, poison, intent missing, abort span missing", 198 run: func(b storage.ReadWriter, rec EvalContext) error { 199 return endTxn(b, rec, true /* commit */, true /* poison */) 200 }, 201 expErr: "cannot poison during a committing EndTxn request", 202 }, 203 { 204 // It is an error for EndTxn to pass Commit = true and Poison = true. 205 name: "end txn, commit, poison, intent missing, abort span present", 206 before: addPrevAbortSpanEntry, 207 run: func(b storage.ReadWriter, rec EvalContext) error { 208 return endTxn(b, rec, true /* commit */, true /* poison */) 209 }, 210 expErr: "cannot poison during a committing EndTxn request", 211 }, 212 { 213 name: "end txn, rollback, no poison, intent present, abort span missing", 214 before: addIntent, 215 run: func(b storage.ReadWriter, rec EvalContext) error { 216 return endTxn(b, rec, false /* commit */, false /* poison */) 217 }, 218 // Not poisoning, should not add an abort span entry. 219 exp: nil, 220 }, 221 { 222 name: "end txn, rollback, no poison, intent present, abort span present", 223 before: compose(addIntent, addPrevAbortSpanEntry), 224 run: func(b storage.ReadWriter, rec EvalContext) error { 225 return endTxn(b, rec, false /* commit */, false /* poison */) 226 }, 227 // Not poisoning, should clean up abort span entry. 228 exp: nil, 229 }, 230 { 231 name: "end txn, rollback, poison, intent present, abort span missing", 232 before: addIntent, 233 run: func(b storage.ReadWriter, rec EvalContext) error { 234 return endTxn(b, rec, false /* commit */, true /* poison */) 235 }, 236 // Poisoning, should add an abort span entry. 237 exp: &newTxnAbortSpanEntry, 238 }, 239 { 240 name: "end txn, rollback, poison, intent present, abort span present", 241 before: compose(addIntent, addPrevAbortSpanEntry), 242 run: func(b storage.ReadWriter, rec EvalContext) error { 243 return endTxn(b, rec, false /* commit */, true /* poison */) 244 }, 245 // Poisoning, should update abort span entry. 246 exp: &newTxnAbortSpanEntry, 247 }, 248 { 249 name: "end txn, commit, no poison, intent present, abort span missing", 250 before: addIntent, 251 run: func(b storage.ReadWriter, rec EvalContext) error { 252 return endTxn(b, rec, true /* commit */, false /* poison */) 253 }, 254 // Not poisoning, should not add an abort span entry. 255 exp: nil, 256 }, 257 { 258 // NOTE: this request doesn't make sense. An abort span shouldn't be 259 // present if the transaction is still committable. 260 name: "end txn, commit, no poison, intent present, abort span present", 261 }, 262 { 263 // It is an error for EndTxn to pass Commit = true and Poison = true. 264 name: "end txn, commit, poison, intent present, abort span missing", 265 before: addIntent, 266 run: func(b storage.ReadWriter, rec EvalContext) error { 267 return endTxn(b, rec, true /* commit */, true /* poison */) 268 }, 269 expErr: "cannot poison during a committing EndTxn request", 270 }, 271 { 272 // It is an error for EndTxn to pass Commit = true and Poison = true. 273 name: "end txn, commit, poison, intent present, abort span present", 274 before: compose(addIntent, addPrevAbortSpanEntry), 275 run: func(b storage.ReadWriter, rec EvalContext) error { 276 return endTxn(b, rec, true /* commit */, true /* poison */) 277 }, 278 expErr: "cannot poison during a committing EndTxn request", 279 }, 280 /////////////////////////////////////////////////////////////////////// 281 // ResolveIntentRequest // 282 /////////////////////////////////////////////////////////////////////// 283 { 284 name: "resolve intent, txn pending, no poison, intent missing, abort span missing", 285 run: func(b storage.ReadWriter, rec EvalContext) error { 286 return resolveIntent(b, rec, roachpb.PENDING, false /* poison */) 287 }, 288 // Not poisoning, should not add an abort span entry. 289 exp: nil, 290 }, 291 { 292 name: "resolve intent, txn pending, no poison, intent missing, abort span present", 293 before: addPrevAbortSpanEntry, 294 run: func(b storage.ReadWriter, rec EvalContext) error { 295 return resolveIntent(b, rec, roachpb.PENDING, false /* poison */) 296 }, 297 // Not aborted, don't touch abort span. 298 exp: &prevTxnAbortSpanEntry, 299 }, 300 { 301 name: "resolve intent, txn pending, no poison, intent present, abort span missing", 302 before: addIntent, 303 run: func(b storage.ReadWriter, rec EvalContext) error { 304 return resolveIntent(b, rec, roachpb.PENDING, false /* poison */) 305 }, 306 // Not poisoning, should not add an abort span entry. 307 exp: nil, 308 }, 309 { 310 name: "resolve intent, txn pending, no poison, intent present, abort span present", 311 before: compose(addIntent, addPrevAbortSpanEntry), 312 run: func(b storage.ReadWriter, rec EvalContext) error { 313 return resolveIntent(b, rec, roachpb.PENDING, false /* poison */) 314 }, 315 // Not aborted, don't touch abort span. 316 exp: &prevTxnAbortSpanEntry, 317 }, 318 { 319 name: "resolve intent, txn pending, poison, intent missing, abort span missing", 320 run: func(b storage.ReadWriter, rec EvalContext) error { 321 return resolveIntent(b, rec, roachpb.PENDING, true /* poison */) 322 }, 323 // Poisoning but not aborted, should not add an abort span entry. 324 exp: nil, 325 }, 326 { 327 name: "resolve intent, txn pending, poison, intent missing, abort span present", 328 before: addPrevAbortSpanEntry, 329 run: func(b storage.ReadWriter, rec EvalContext) error { 330 return resolveIntent(b, rec, roachpb.PENDING, true /* poison */) 331 }, 332 // Not aborted, don't touch abort span. 333 exp: &prevTxnAbortSpanEntry, 334 }, 335 { 336 name: "resolve intent, txn pending, poison, intent present, abort span missing", 337 before: addIntent, 338 run: func(b storage.ReadWriter, rec EvalContext) error { 339 return resolveIntent(b, rec, roachpb.PENDING, true /* poison */) 340 }, 341 // Poisoning but not aborted, should not add an abort span entry. 342 exp: nil, 343 }, 344 { 345 name: "resolve intent, txn pending, poison, intent present, abort span present", 346 before: compose(addIntent, addPrevAbortSpanEntry), 347 run: func(b storage.ReadWriter, rec EvalContext) error { 348 return resolveIntent(b, rec, roachpb.PENDING, true /* poison */) 349 }, 350 // Not aborted, don't touch abort span. 351 exp: &prevTxnAbortSpanEntry, 352 }, 353 { 354 name: "resolve intent, txn aborted, no poison, intent missing, abort span missing", 355 run: func(b storage.ReadWriter, rec EvalContext) error { 356 return resolveIntent(b, rec, roachpb.ABORTED, false /* poison */) 357 }, 358 // Not poisoning, should not add an abort span entry. 359 exp: nil, 360 }, 361 { 362 name: "resolve intent, txn aborted, no poison, intent missing, abort span present", 363 before: addPrevAbortSpanEntry, 364 run: func(b storage.ReadWriter, rec EvalContext) error { 365 return resolveIntent(b, rec, roachpb.ABORTED, false /* poison */) 366 }, 367 // Not poisoning, should clean up abort span entry. 368 exp: nil, 369 }, 370 { 371 name: "resolve intent, txn aborted, no poison, intent present, abort span missing", 372 before: addIntent, 373 run: func(b storage.ReadWriter, rec EvalContext) error { 374 return resolveIntent(b, rec, roachpb.ABORTED, false /* poison */) 375 }, 376 // Not poisoning, should not add an abort span entry. 377 exp: nil, 378 }, 379 { 380 name: "resolve intent, txn aborted, no poison, intent present, abort span present", 381 before: compose(addIntent, addPrevAbortSpanEntry), 382 run: func(b storage.ReadWriter, rec EvalContext) error { 383 return resolveIntent(b, rec, roachpb.ABORTED, false /* poison */) 384 }, 385 // Not poisoning, should clean up abort span entry. 386 exp: nil, 387 }, 388 { 389 name: "resolve intent, txn aborted, poison, intent missing, abort span missing", 390 run: func(b storage.ReadWriter, rec EvalContext) error { 391 return resolveIntent(b, rec, roachpb.ABORTED, true /* poison */) 392 }, 393 // Poisoning, but no intents found, should not add an abort span entry. 394 exp: nil, 395 }, 396 { 397 name: "resolve intent, txn aborted, poison, intent missing, abort span present", 398 before: addPrevAbortSpanEntry, 399 run: func(b storage.ReadWriter, rec EvalContext) error { 400 return resolveIntent(b, rec, roachpb.ABORTED, true /* poison */) 401 }, 402 // Poisoning, but no intents found, don't touch abort span. 403 exp: &prevTxnAbortSpanEntry, 404 }, 405 { 406 name: "resolve intent, txn aborted, poison, intent present, abort span missing", 407 before: addIntent, 408 run: func(b storage.ReadWriter, rec EvalContext) error { 409 return resolveIntent(b, rec, roachpb.ABORTED, true /* poison */) 410 }, 411 // Poisoning, should add an abort span entry. 412 exp: &newTxnAbortSpanEntry, 413 }, 414 { 415 name: "resolve intent, txn aborted, poison, intent present, abort span present", 416 before: compose(addIntent, addPrevAbortSpanEntry), 417 run: func(b storage.ReadWriter, rec EvalContext) error { 418 return resolveIntent(b, rec, roachpb.ABORTED, true /* poison */) 419 }, 420 // Poisoning, should update abort span entry. 421 exp: &newTxnAbortSpanEntry, 422 }, 423 { 424 name: "resolve intent, txn committed, no poison, intent missing, abort span missing", 425 run: func(b storage.ReadWriter, rec EvalContext) error { 426 return resolveIntent(b, rec, roachpb.COMMITTED, false /* poison */) 427 }, 428 // Not poisoning, should not add an abort span entry. 429 exp: nil, 430 }, 431 { 432 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 433 // txn to have an abort span before its intents are cleaned up. 434 name: "resolve intent, txn committed, no poison, intent missing, abort span present", 435 }, 436 { 437 name: "resolve intent, txn committed, no poison, intent present, abort span missing", 438 before: addIntent, 439 run: func(b storage.ReadWriter, rec EvalContext) error { 440 return resolveIntent(b, rec, roachpb.COMMITTED, false /* poison */) 441 }, 442 // Not poisoning, should not add an abort span entry. 443 exp: nil, 444 }, 445 { 446 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 447 // txn to have an abort span before its intents are cleaned up. 448 name: "resolve intent, txn committed, no poison, intent present, abort span present", 449 }, 450 { 451 name: "resolve intent, txn committed, poison, intent missing, abort span missing", 452 run: func(b storage.ReadWriter, rec EvalContext) error { 453 return resolveIntent(b, rec, roachpb.COMMITTED, true /* poison */) 454 }, 455 // Poisoning but not aborted, should not add an abort span entry. 456 exp: nil, 457 }, 458 { 459 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 460 // txn to have an abort span before its intents are cleaned up. 461 name: "resolve intent, txn committed, poison, intent missing, abort span present", 462 }, 463 { 464 name: "resolve intent, txn committed, poison, intent present, abort span missing", 465 before: addIntent, 466 run: func(b storage.ReadWriter, rec EvalContext) error { 467 return resolveIntent(b, rec, roachpb.COMMITTED, true /* poison */) 468 }, 469 // Poisoning but not aborted, should not add an abort span entry. 470 exp: nil, 471 }, 472 { 473 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 474 // txn to have an abort span before its intents are cleaned up. 475 name: "resolve intent, txn committed, poison, intent present, abort span present", 476 }, 477 /////////////////////////////////////////////////////////////////////// 478 // ResolveIntentRangeRequest // 479 /////////////////////////////////////////////////////////////////////// 480 { 481 name: "resolve intent range, txn pending, no poison, intent missing, abort span missing", 482 run: func(b storage.ReadWriter, rec EvalContext) error { 483 return resolveIntentRange(b, rec, roachpb.PENDING, false /* poison */) 484 }, 485 // Not poisoning, should not add an abort span entry. 486 exp: nil, 487 }, 488 { 489 name: "resolve intent range, txn pending, no poison, intent missing, abort span present", 490 before: addPrevAbortSpanEntry, 491 run: func(b storage.ReadWriter, rec EvalContext) error { 492 return resolveIntentRange(b, rec, roachpb.PENDING, false /* poison */) 493 }, 494 // Not aborted, don't touch abort span. 495 exp: &prevTxnAbortSpanEntry, 496 }, 497 { 498 name: "resolve intent range, txn pending, no poison, intent present, abort span missing", 499 before: addIntent, 500 run: func(b storage.ReadWriter, rec EvalContext) error { 501 return resolveIntentRange(b, rec, roachpb.PENDING, false /* poison */) 502 }, 503 // Not poisoning, should not add an abort span entry. 504 exp: nil, 505 }, 506 { 507 name: "resolve intent range, txn pending, no poison, intent present, abort span present", 508 before: compose(addIntent, addPrevAbortSpanEntry), 509 run: func(b storage.ReadWriter, rec EvalContext) error { 510 return resolveIntentRange(b, rec, roachpb.PENDING, false /* poison */) 511 }, 512 // Not aborted, don't touch abort span. 513 exp: &prevTxnAbortSpanEntry, 514 }, 515 { 516 name: "resolve intent range, txn pending, poison, intent missing, abort span missing", 517 run: func(b storage.ReadWriter, rec EvalContext) error { 518 return resolveIntentRange(b, rec, roachpb.PENDING, true /* poison */) 519 }, 520 // Poisoning but not aborted, should not add an abort span entry. 521 exp: nil, 522 }, 523 { 524 name: "resolve intent range, txn pending, poison, intent missing, abort span present", 525 before: addPrevAbortSpanEntry, 526 run: func(b storage.ReadWriter, rec EvalContext) error { 527 return resolveIntentRange(b, rec, roachpb.PENDING, true /* poison */) 528 }, 529 // Not aborted, don't touch abort span. 530 exp: &prevTxnAbortSpanEntry, 531 }, 532 { 533 name: "resolve intent range, txn pending, poison, intent present, abort span missing", 534 before: addIntent, 535 run: func(b storage.ReadWriter, rec EvalContext) error { 536 return resolveIntentRange(b, rec, roachpb.PENDING, true /* poison */) 537 }, 538 // Poisoning but not aborted, should not add an abort span entry. 539 exp: nil, 540 }, 541 { 542 name: "resolve intent range, txn pending, poison, intent present, abort span present", 543 before: compose(addIntent, addPrevAbortSpanEntry), 544 run: func(b storage.ReadWriter, rec EvalContext) error { 545 return resolveIntentRange(b, rec, roachpb.PENDING, true /* poison */) 546 }, 547 // Not aborted, don't touch abort span. 548 exp: &prevTxnAbortSpanEntry, 549 }, 550 { 551 name: "resolve intent range, txn aborted, no poison, intent missing, abort span missing", 552 run: func(b storage.ReadWriter, rec EvalContext) error { 553 return resolveIntentRange(b, rec, roachpb.ABORTED, false /* poison */) 554 }, 555 // Not poisoning, should not add an abort span entry. 556 exp: nil, 557 }, 558 { 559 name: "resolve intent range, txn aborted, no poison, intent missing, abort span present", 560 before: addPrevAbortSpanEntry, 561 run: func(b storage.ReadWriter, rec EvalContext) error { 562 return resolveIntentRange(b, rec, roachpb.ABORTED, false /* poison */) 563 }, 564 // Not poisoning, should clean up abort span entry. 565 exp: nil, 566 }, 567 { 568 name: "resolve intent range, txn aborted, no poison, intent present, abort span missing", 569 before: addIntent, 570 run: func(b storage.ReadWriter, rec EvalContext) error { 571 return resolveIntentRange(b, rec, roachpb.ABORTED, false /* poison */) 572 }, 573 // Not poisoning, should not add an abort span entry. 574 exp: nil, 575 }, 576 { 577 name: "resolve intent range, txn aborted, no poison, intent present, abort span present", 578 before: compose(addIntent, addPrevAbortSpanEntry), 579 run: func(b storage.ReadWriter, rec EvalContext) error { 580 return resolveIntentRange(b, rec, roachpb.ABORTED, false /* poison */) 581 }, 582 // Not poisoning, should clean up abort span entry. 583 exp: nil, 584 }, 585 { 586 name: "resolve intent range, txn aborted, poison, intent missing, abort span missing", 587 run: func(b storage.ReadWriter, rec EvalContext) error { 588 return resolveIntentRange(b, rec, roachpb.ABORTED, true /* poison */) 589 }, 590 // Poisoning, but no intents found, should not add an abort span entry. 591 exp: nil, 592 }, 593 { 594 name: "resolve intent range, txn aborted, poison, intent missing, abort span present", 595 before: addPrevAbortSpanEntry, 596 run: func(b storage.ReadWriter, rec EvalContext) error { 597 return resolveIntentRange(b, rec, roachpb.ABORTED, true /* poison */) 598 }, 599 // Poisoning, but no intents found, don't touch abort span. 600 exp: &prevTxnAbortSpanEntry, 601 }, 602 { 603 name: "resolve intent range, txn aborted, poison, intent present, abort span missing", 604 before: addIntent, 605 run: func(b storage.ReadWriter, rec EvalContext) error { 606 return resolveIntentRange(b, rec, roachpb.ABORTED, true /* poison */) 607 }, 608 // Poisoning, should add an abort span entry. 609 exp: &newTxnAbortSpanEntry, 610 }, 611 { 612 name: "resolve intent range, txn aborted, poison, intent present, abort span present", 613 before: compose(addIntent, addPrevAbortSpanEntry), 614 run: func(b storage.ReadWriter, rec EvalContext) error { 615 return resolveIntentRange(b, rec, roachpb.ABORTED, true /* poison */) 616 }, 617 // Poisoning, should update abort span entry. 618 exp: &newTxnAbortSpanEntry, 619 }, 620 { 621 name: "resolve intent range, txn committed, no poison, intent missing, abort span missing", 622 run: func(b storage.ReadWriter, rec EvalContext) error { 623 return resolveIntentRange(b, rec, roachpb.COMMITTED, false /* poison */) 624 }, 625 // Not poisoning, should not add an abort span entry. 626 exp: nil, 627 }, 628 { 629 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 630 // txn to have an abort span before its intents are cleaned up. 631 name: "resolve intent range, txn committed, no poison, intent missing, abort span present", 632 }, 633 { 634 name: "resolve intent range, txn committed, no poison, intent present, abort span missing", 635 before: addIntent, 636 run: func(b storage.ReadWriter, rec EvalContext) error { 637 return resolveIntentRange(b, rec, roachpb.COMMITTED, false /* poison */) 638 }, 639 // Not poisoning, should not add an abort span entry. 640 exp: nil, 641 }, 642 { 643 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 644 // txn to have an abort span before its intents are cleaned up. 645 name: "resolve intent range, txn committed, no poison, intent present, abort span present", 646 }, 647 { 648 name: "resolve intent range, txn committed, poison, intent missing, abort span missing", 649 run: func(b storage.ReadWriter, rec EvalContext) error { 650 return resolveIntentRange(b, rec, roachpb.COMMITTED, true /* poison */) 651 }, 652 // Poisoning but not aborted, should not add an abort span entry. 653 exp: nil, 654 }, 655 { 656 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 657 // txn to have an abort span before its intents are cleaned up. 658 name: "resolve intent range, txn committed, poison, intent missing, abort span present", 659 }, 660 { 661 name: "resolve intent range, txn committed, poison, intent present, abort span missing", 662 before: addIntent, 663 run: func(b storage.ReadWriter, rec EvalContext) error { 664 return resolveIntentRange(b, rec, roachpb.COMMITTED, true /* poison */) 665 }, 666 // Poisoning but not aborted, should not add an abort span entry. 667 exp: nil, 668 }, 669 { 670 // NOTE: this case doesn't make sense. It shouldn't be possible for a committed 671 // txn to have an abort span before its intents are cleaned up. 672 name: "resolve intent range, txn committed, poison, intent present, abort span present", 673 }, 674 } 675 for _, c := range testCases { 676 t.Run(c.name, func(t *testing.T) { 677 if c.run == nil { 678 t.Skip("invalid test case") 679 } 680 681 db := storage.NewDefaultInMem() 682 defer db.Close() 683 batch := db.NewBatch() 684 defer batch.Close() 685 evalCtx := &MockEvalCtx{ 686 Desc: &desc, 687 AbortSpan: as, 688 CanCreateTxn: func() (bool, hlc.Timestamp, roachpb.TransactionAbortedReason) { 689 return true, hlc.Timestamp{}, 0 690 }, 691 } 692 693 if c.before != nil { 694 require.NoError(t, c.before(batch, evalCtx.EvalContext())) 695 } 696 697 err := c.run(batch, evalCtx.EvalContext()) 698 if c.expErr != "" { 699 require.Regexp(t, c.expErr, err) 700 } else { 701 require.NoError(t, err) 702 703 var curEntry roachpb.AbortSpanEntry 704 exists, err := as.Get(ctx, batch, txn.ID, &curEntry) 705 require.NoError(t, err) 706 require.Equal(t, c.exp != nil, exists) 707 if exists { 708 require.Equal(t, *c.exp, curEntry) 709 } 710 } 711 }) 712 } 713 }