github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batch_spanset_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package kvserver 12 13 import ( 14 "bytes" 15 "context" 16 "reflect" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" 20 "github.com/cockroachdb/cockroach/pkg/roachpb" 21 "github.com/cockroachdb/cockroach/pkg/storage" 22 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 23 "github.com/cockroachdb/cockroach/pkg/testutils" 24 "github.com/cockroachdb/cockroach/pkg/util/hlc" 25 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 26 "github.com/cockroachdb/errors" 27 ) 28 29 func TestSpanSetBatchBoundaries(t *testing.T) { 30 defer leaktest.AfterTest(t)() 31 eng := storage.NewDefaultInMem() 32 defer eng.Close() 33 34 var ss spanset.SpanSet 35 ss.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: roachpb.Key("c"), EndKey: roachpb.Key("g")}) 36 outsideKey := storage.MakeMVCCMetadataKey(roachpb.Key("a")) 37 outsideKey2 := storage.MakeMVCCMetadataKey(roachpb.Key("b")) 38 outsideKey3 := storage.MakeMVCCMetadataKey(roachpb.Key("g")) 39 outsideKey4 := storage.MakeMVCCMetadataKey(roachpb.Key("m")) 40 insideKey := storage.MakeMVCCMetadataKey(roachpb.Key("c")) 41 insideKey2 := storage.MakeMVCCMetadataKey(roachpb.Key("d")) 42 insideKey3 := storage.MakeMVCCMetadataKey(roachpb.Key("f")) 43 44 // Write values outside the range that we can try to read later. 45 if err := eng.Put(outsideKey, []byte("value")); err != nil { 46 t.Fatalf("direct write failed: %+v", err) 47 } 48 if err := eng.Put(outsideKey3, []byte("value")); err != nil { 49 t.Fatalf("direct write failed: %+v", err) 50 } 51 52 batch := spanset.NewBatch(eng.NewBatch(), &ss) 53 defer batch.Close() 54 55 // Writes inside the range work. Write twice for later read testing. 56 if err := batch.Put(insideKey, []byte("value")); err != nil { 57 t.Fatalf("failed to write inside the range: %+v", err) 58 } 59 if err := batch.Put(insideKey2, []byte("value2")); err != nil { 60 t.Fatalf("failed to write inside the range: %+v", err) 61 } 62 63 // Writes outside the range fail. We try to cover all write methods 64 // in the failure case to make sure the CheckAllowed call is 65 // present, but we don't attempt successful versions of all 66 // methods since those are harder to set up. 67 isWriteSpanErr := func(err error) bool { 68 return testutils.IsError(err, "cannot write undeclared span") 69 } 70 71 t.Run("writes before range", func(t *testing.T) { 72 if err := batch.Clear(outsideKey); !isWriteSpanErr(err) { 73 t.Errorf("Clear: unexpected error %v", err) 74 } 75 if err := batch.ClearRange(outsideKey, outsideKey2); !isWriteSpanErr(err) { 76 t.Errorf("ClearRange: unexpected error %v", err) 77 } 78 { 79 iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 80 err := batch.ClearIterRange(iter, outsideKey.Key, outsideKey2.Key) 81 iter.Close() 82 if !isWriteSpanErr(err) { 83 t.Errorf("ClearIterRange: unexpected error %v", err) 84 } 85 } 86 if err := batch.Merge(outsideKey, nil); !isWriteSpanErr(err) { 87 t.Errorf("Merge: unexpected error %v", err) 88 } 89 if err := batch.Put(outsideKey, nil); !isWriteSpanErr(err) { 90 t.Errorf("Put: unexpected error %v", err) 91 } 92 }) 93 94 t.Run("writes after range", func(t *testing.T) { 95 if err := batch.Clear(outsideKey3); !isWriteSpanErr(err) { 96 t.Errorf("Clear: unexpected error %v", err) 97 } 98 if err := batch.ClearRange(insideKey2, outsideKey4); !isWriteSpanErr(err) { 99 t.Errorf("ClearRange: unexpected error %v", err) 100 } 101 { 102 iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 103 err := batch.ClearIterRange(iter, insideKey2.Key, outsideKey4.Key) 104 iter.Close() 105 if !isWriteSpanErr(err) { 106 t.Errorf("ClearIterRange: unexpected error %v", err) 107 } 108 } 109 if err := batch.Merge(outsideKey3, nil); !isWriteSpanErr(err) { 110 t.Errorf("Merge: unexpected error %v", err) 111 } 112 if err := batch.Put(outsideKey3, nil); !isWriteSpanErr(err) { 113 t.Errorf("Put: unexpected error %v", err) 114 } 115 }) 116 117 t.Run("reads inside range", func(t *testing.T) { 118 //lint:ignore SA1019 historical usage of deprecated batch.Get is OK 119 if value, err := batch.Get(insideKey); err != nil { 120 t.Errorf("failed to read inside the range: %+v", err) 121 } else if !bytes.Equal(value, []byte("value")) { 122 t.Errorf("failed to read previously written value, got %q", value) 123 } 124 //lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK 125 if _, _, _, err := batch.GetProto(insideKey, nil); err != nil { 126 t.Errorf("GetProto: unexpected error %v", err) 127 } 128 if err := batch.Iterate(insideKey.Key, insideKey2.Key, 129 func(v storage.MVCCKeyValue) (bool, error) { 130 return false, nil 131 }, 132 ); err != nil { 133 t.Errorf("Iterate: unexpected error %v", err) 134 } 135 }) 136 137 // Reads outside the range fail. 138 isReadSpanErr := func(err error) bool { 139 return testutils.IsError(err, "cannot read undeclared span") 140 } 141 142 t.Run("reads before range", func(t *testing.T) { 143 //lint:ignore SA1019 historical usage of deprecated batch.Get is OK 144 if _, err := batch.Get(outsideKey); !isReadSpanErr(err) { 145 t.Errorf("Get: unexpected error %v", err) 146 } 147 //lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK 148 if _, _, _, err := batch.GetProto(outsideKey, nil); !isReadSpanErr(err) { 149 t.Errorf("GetProto: unexpected error %v", err) 150 } 151 if err := batch.Iterate(outsideKey.Key, insideKey2.Key, 152 func(v storage.MVCCKeyValue) (bool, error) { 153 return false, errors.Errorf("unexpected callback: %v", v) 154 }, 155 ); !isReadSpanErr(err) { 156 t.Errorf("Iterate: unexpected error %v", err) 157 } 158 }) 159 160 t.Run("reads after range", func(t *testing.T) { 161 //lint:ignore SA1019 historical usage of deprecated batch.Get is OK 162 if _, err := batch.Get(outsideKey3); !isReadSpanErr(err) { 163 t.Errorf("Get: unexpected error %v", err) 164 } 165 //lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK 166 if _, _, _, err := batch.GetProto(outsideKey3, nil); !isReadSpanErr(err) { 167 t.Errorf("GetProto: unexpected error %v", err) 168 } 169 if err := batch.Iterate(insideKey2.Key, outsideKey4.Key, 170 func(v storage.MVCCKeyValue) (bool, error) { 171 return false, errors.Errorf("unexpected callback: %v", v) 172 }, 173 ); !isReadSpanErr(err) { 174 t.Errorf("Iterate: unexpected error %v", err) 175 } 176 }) 177 178 t.Run("forward scans", func(t *testing.T) { 179 iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 180 defer iter.Close() 181 182 // Iterators check boundaries on seek and next/prev 183 iter.SeekGE(outsideKey) 184 if _, err := iter.Valid(); !isReadSpanErr(err) { 185 t.Fatalf("Seek: unexpected error %v", err) 186 } 187 iter.SeekGE(outsideKey3) 188 if _, err := iter.Valid(); !isReadSpanErr(err) { 189 t.Fatalf("Seek: unexpected error %v", err) 190 } 191 // Seeking back in bounds restores validity. 192 iter.SeekGE(insideKey) 193 if ok, err := iter.Valid(); !ok || err != nil { 194 t.Fatalf("expected valid iterator, err=%v", err) 195 } 196 if !reflect.DeepEqual(iter.Key(), insideKey) { 197 t.Fatalf("expected key %s, got %s", insideKey, iter.Key()) 198 } 199 iter.Next() 200 if ok, err := iter.Valid(); !ok || err != nil { 201 t.Fatalf("expected valid iterator, err=%v", err) 202 } 203 if !reflect.DeepEqual(iter.Key(), insideKey2) { 204 t.Fatalf("expected key %s, got %s", insideKey2, iter.Key()) 205 } 206 // Scan out of bounds. 207 iter.Next() 208 if ok, err := iter.Valid(); ok { 209 t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key()) 210 } else if err != nil { 211 // Scanning out of bounds sets Valid() to false but is not an error. 212 t.Errorf("unexpected error on iterator: %+v", err) 213 } 214 }) 215 216 // Same test in reverse. We commit the batch and wrap an iterator on 217 // the raw engine because we don't support bidirectional iteration 218 // over a pending batch. 219 if err := batch.Commit(true); err != nil { 220 t.Fatal(err) 221 } 222 223 t.Run("reverse scans", func(t *testing.T) { 224 iter := spanset.NewIterator(eng.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}), &ss) 225 defer iter.Close() 226 iter.SeekLT(outsideKey4) 227 if _, err := iter.Valid(); !isReadSpanErr(err) { 228 t.Fatalf("SeekLT: unexpected error %v", err) 229 } 230 // Seeking back in bounds restores validity. 231 iter.SeekLT(insideKey3) 232 if ok, err := iter.Valid(); !ok || err != nil { 233 t.Fatalf("expected valid iterator, err=%v", err) 234 } 235 if !reflect.DeepEqual(iter.Key(), insideKey2) { 236 t.Fatalf("expected key %s, got %s", insideKey2, iter.Key()) 237 } 238 iter.Prev() 239 if ok, err := iter.Valid(); !ok || err != nil { 240 t.Fatalf("expected valid iterator, err=%v", err) 241 } 242 if !reflect.DeepEqual(iter.Key(), insideKey) { 243 t.Fatalf("expected key %s, got %s", insideKey, iter.Key()) 244 } 245 // Scan out of bounds. 246 iter.Prev() 247 if ok, err := iter.Valid(); ok { 248 t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key()) 249 } else if err != nil { 250 t.Errorf("unexpected error on iterator: %+v", err) 251 } 252 253 // Seeking back in bounds restores validity. 254 iter.SeekLT(insideKey2) 255 if ok, err := iter.Valid(); !ok || err != nil { 256 t.Fatalf("expected valid iterator, err=%v", err) 257 } 258 // SeekLT to the lower bound is invalid. 259 iter.SeekLT(insideKey) 260 if ok, err := iter.Valid(); ok { 261 t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key()) 262 } else if !isReadSpanErr(err) { 263 t.Fatalf("SeekLT: unexpected error %v", err) 264 } 265 // SeekLT to upper bound restores validity. 266 iter.SeekLT(outsideKey3) 267 if ok, err := iter.Valid(); !ok || err != nil { 268 t.Fatalf("expected valid iterator, err=%v", err) 269 } 270 }) 271 } 272 273 func TestSpanSetBatchTimestamps(t *testing.T) { 274 defer leaktest.AfterTest(t)() 275 eng := storage.NewDefaultInMem() 276 defer eng.Close() 277 278 var ss spanset.SpanSet 279 ss.AddMVCC(spanset.SpanReadOnly, 280 roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("c")}, hlc.Timestamp{WallTime: 2}) 281 ss.AddMVCC(spanset.SpanReadWrite, 282 roachpb.Span{Key: roachpb.Key("d"), EndKey: roachpb.Key("f")}, hlc.Timestamp{WallTime: 2}) 283 284 rkey := storage.MakeMVCCMetadataKey(roachpb.Key("b")) 285 wkey := storage.MakeMVCCMetadataKey(roachpb.Key("e")) 286 287 value := []byte("value") 288 289 // Write value that we can try to read later. 290 if err := eng.Put(rkey, value); err != nil { 291 t.Fatalf("direct write failed: %+v", err) 292 } 293 294 batchNonMVCC := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 0}) 295 defer batchNonMVCC.Close() 296 297 batchBefore := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 1}) 298 defer batchBefore.Close() 299 300 batchDuring := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 2}) 301 defer batchDuring.Close() 302 303 batchAfter := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 3}) 304 defer batchAfter.Close() 305 306 // Writes. 307 for _, batch := range []storage.Batch{batchAfter, batchDuring} { 308 if err := batch.Put(wkey, value); err != nil { 309 t.Fatalf("failed to write inside the range at same or greater ts than latch declaration: %+v", err) 310 } 311 } 312 313 for _, batch := range []storage.Batch{batchBefore, batchNonMVCC} { 314 if err := batch.Put(wkey, value); err == nil { 315 t.Fatalf("was able to write inside the range at ts less than latch declaration: %+v", err) 316 } 317 } 318 319 // We try to cover all write methods in the failure case to make sure 320 // the CheckAllowedAt call is present, but we don't attempt to successful 321 // versions of all methods since those are harder to set up. 322 isWriteSpanErr := func(err error) bool { 323 return testutils.IsError(err, "cannot write undeclared span") 324 } 325 326 for _, batch := range []storage.Batch{batchBefore, batchNonMVCC} { 327 if err := batch.Clear(wkey); !isWriteSpanErr(err) { 328 t.Errorf("Clear: unexpected error %v", err) 329 } 330 { 331 iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 332 err := batch.ClearIterRange(iter, wkey.Key, wkey.Key) 333 iter.Close() 334 if !isWriteSpanErr(err) { 335 t.Errorf("ClearIterRange: unexpected error %v", err) 336 } 337 } 338 if err := batch.Merge(wkey, nil); !isWriteSpanErr(err) { 339 t.Errorf("Merge: unexpected error %v", err) 340 } 341 if err := batch.Put(wkey, nil); !isWriteSpanErr(err) { 342 t.Errorf("Put: unexpected error %v", err) 343 } 344 } 345 346 // Reads. 347 for _, batch := range []storage.Batch{batchBefore, batchDuring} { 348 //lint:ignore SA1019 historical usage of deprecated batch.Get is OK 349 if res, err := batch.Get(rkey); err != nil { 350 t.Errorf("failed to read inside the range: %+v", err) 351 } else if !bytes.Equal(res, value) { 352 t.Errorf("failed to read previously written value, got %q", res) 353 } 354 } 355 356 isReadSpanErr := func(err error) bool { 357 return testutils.IsError(err, "cannot read undeclared span") 358 } 359 360 for _, batch := range []storage.Batch{batchAfter, batchNonMVCC} { 361 //lint:ignore SA1019 historical usage of deprecated batch.Get is OK 362 if _, err := batch.Get(rkey); !isReadSpanErr(err) { 363 t.Errorf("Get: unexpected error %v", err) 364 } 365 366 //lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK 367 if _, _, _, err := batch.GetProto(rkey, nil); !isReadSpanErr(err) { 368 t.Errorf("GetProto: unexpected error %v", err) 369 } 370 if err := batch.Iterate(rkey.Key, rkey.Key, 371 func(v storage.MVCCKeyValue) (bool, error) { 372 return false, errors.Errorf("unexpected callback: %v", v) 373 }, 374 ); !isReadSpanErr(err) { 375 t.Errorf("Iterate: unexpected error %v", err) 376 } 377 } 378 } 379 380 func TestSpanSetIteratorTimestamps(t *testing.T) { 381 defer leaktest.AfterTest(t)() 382 eng := storage.NewDefaultInMem() 383 defer eng.Close() 384 385 var ss spanset.SpanSet 386 ss.AddMVCC(spanset.SpanReadOnly, roachpb.Span{ 387 Key: roachpb.Key("a"), EndKey: roachpb.Key("c")}, hlc.Timestamp{WallTime: 1}) 388 ss.AddMVCC(spanset.SpanReadOnly, roachpb.Span{ 389 Key: roachpb.Key("c"), EndKey: roachpb.Key("e")}, hlc.Timestamp{WallTime: 2}) 390 391 k1, v1 := storage.MakeMVCCMetadataKey(roachpb.Key("b")), []byte("b-value") 392 k2, v2 := storage.MakeMVCCMetadataKey(roachpb.Key("d")), []byte("d-value") 393 394 // Write values that we can try to read later. 395 if err := eng.Put(k1, v1); err != nil { 396 t.Fatalf("direct write failed: %+v", err) 397 } 398 if err := eng.Put(k2, v2); err != nil { 399 t.Fatalf("direct write failed: %+v", err) 400 } 401 402 batchNonMVCC := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 0}) 403 defer batchNonMVCC.Close() 404 405 batchAt1 := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 1}) 406 defer batchAt1.Close() 407 408 batchAt2 := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 2}) 409 defer batchAt2.Close() 410 411 batchAt3 := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 3}) 412 defer batchAt3.Close() 413 414 func() { 415 // When accessing at t=1, we're able to read through latches declared at t=1 and t=2. 416 iter := batchAt1.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 417 defer iter.Close() 418 419 iter.SeekGE(k1) 420 if ok, err := iter.Valid(); !ok { 421 t.Fatalf("expected valid iterator, err=%v", err) 422 } 423 if !reflect.DeepEqual(iter.Key(), k1) { 424 t.Fatalf("expected key %s, got %s", k1, iter.Key()) 425 } 426 427 iter.Next() 428 if ok, err := iter.Valid(); !ok { 429 t.Fatalf("expected valid iterator, err=%v", err) 430 } 431 if !reflect.DeepEqual(iter.Key(), k2) { 432 t.Fatalf("expected key %s, got %s", k2, iter.Key()) 433 } 434 }() 435 436 { 437 // When accessing at t=2, we're only able to read through the latch declared at t=2. 438 iter := batchAt2.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 439 defer iter.Close() 440 441 iter.SeekGE(k1) 442 if ok, _ := iter.Valid(); ok { 443 t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key()) 444 } 445 446 iter.SeekGE(k2) 447 if ok, err := iter.Valid(); !ok { 448 t.Fatalf("expected valid iterator, err=%v", err) 449 } 450 if !reflect.DeepEqual(iter.Key(), k2) { 451 t.Fatalf("expected key %s, got %s", k2, iter.Key()) 452 } 453 } 454 455 for _, batch := range []storage.Batch{batchAt3, batchNonMVCC} { 456 // When accessing at t=3, we're unable to read through any of the declared latches. 457 // Same is true when accessing without a timestamp. 458 iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 459 defer iter.Close() 460 461 iter.SeekGE(k1) 462 if ok, _ := iter.Valid(); ok { 463 t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key()) 464 } 465 466 iter.SeekGE(k2) 467 if ok, _ := iter.Valid(); ok { 468 t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key()) 469 } 470 } 471 } 472 473 func TestSpanSetNonMVCCBatch(t *testing.T) { 474 defer leaktest.AfterTest(t)() 475 eng := storage.NewDefaultInMem() 476 defer eng.Close() 477 478 var ss spanset.SpanSet 479 ss.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("c")}) 480 ss.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: roachpb.Key("d"), EndKey: roachpb.Key("f")}) 481 482 rkey := storage.MakeMVCCMetadataKey(roachpb.Key("b")) 483 wkey := storage.MakeMVCCMetadataKey(roachpb.Key("e")) 484 485 value := []byte("value") 486 487 // Write value that we can try to read later. 488 if err := eng.Put(rkey, value); err != nil { 489 t.Fatalf("direct write failed: %+v", err) 490 } 491 492 batchNonMVCC := spanset.NewBatch(eng.NewBatch(), &ss) 493 defer batchNonMVCC.Close() 494 495 batchMVCC := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 1}) 496 defer batchMVCC.Close() 497 498 // Writes. 499 for _, batch := range []storage.Batch{batchNonMVCC, batchMVCC} { 500 if err := batch.Put(wkey, value); err != nil { 501 t.Fatalf("write disallowed through non-MVCC latch: %+v", err) 502 } 503 } 504 505 // Reads. 506 for _, batch := range []storage.Batch{batchNonMVCC, batchMVCC} { 507 //lint:ignore SA1019 historical usage of deprecated batch.Get is OK 508 if res, err := batch.Get(rkey); err != nil { 509 t.Errorf("read disallowed through non-MVCC latch: %+v", err) 510 } else if !bytes.Equal(res, value) { 511 t.Errorf("failed to read previously written value, got %q", res) 512 } 513 } 514 } 515 516 // TestSpanSetMVCCResolveWriteIntentRangeUsingIter verifies that 517 // MVCCResolveWriteIntentRangeUsingIter does not stray outside of the passed-in 518 // key range (which it only used to do in this corner case tested here). 519 // 520 // See #20894. 521 func TestSpanSetMVCCResolveWriteIntentRangeUsingIter(t *testing.T) { 522 defer leaktest.AfterTest(t)() 523 eng := storage.NewDefaultInMem() 524 defer eng.Close() 525 526 ctx := context.Background() 527 528 value := roachpb.MakeValueFromString("irrelevant") 529 530 if err := storage.MVCCPut( 531 ctx, 532 eng, 533 nil, // ms 534 roachpb.Key("b"), 535 hlc.Timestamp{WallTime: 10}, // irrelevant 536 value, 537 nil, // txn 538 ); err != nil { 539 t.Fatal(err) 540 } 541 542 var ss spanset.SpanSet 543 ss.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("b\x00")}) 544 545 batch := spanset.NewBatch(eng.NewBatch(), &ss) 546 defer batch.Close() 547 548 intent := roachpb.LockUpdate{ 549 Span: roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("b\x00")}, 550 Txn: enginepb.TxnMeta{}, // unused 551 Status: roachpb.PENDING, 552 } 553 554 iterAndBuf := storage.GetIterAndBuf(batch, storage.IterOptions{UpperBound: intent.Span.EndKey}) 555 defer iterAndBuf.Cleanup() 556 557 if _, _, err := storage.MVCCResolveWriteIntentRangeUsingIter( 558 ctx, batch, iterAndBuf, nil /* ms */, intent, 0, 559 ); err != nil { 560 t.Fatal(err) 561 } 562 }