@@ -38,10 +38,20 @@ import (
38
38
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
39
39
"github.com/cockroachdb/cockroach/pkg/util/log"
40
40
"github.com/cockroachdb/cockroach/pkg/util/randutil"
41
+ "github.com/cockroachdb/cockroach/pkg/util/syncutil"
42
+ "github.com/cockroachdb/cockroach/pkg/util/uuid"
41
43
"github.com/cockroachdb/errors"
42
44
"github.com/stretchr/testify/require"
43
45
)
44
46
47
+ func checkGetResults (t * testing.T , expected map [string ][]byte , results ... kv.Result ) {
48
+ for _ , result := range results {
49
+ require .Equal (t , 1 , len (result .Rows ))
50
+ require .Equal (t , expected [string (result .Rows [0 ].Key )], result .Rows [0 ].ValueBytes ())
51
+ }
52
+ require .Len (t , expected , len (results ))
53
+ }
54
+
45
55
// TestTxnDBBasics verifies that a simple transaction can be run and
46
56
// either committed or aborted. On commit, mutations are visible; on
47
57
// abort, mutations are never visible. During the txn, verify that
@@ -2371,3 +2381,82 @@ func TestLeafTransactionAdmissionHeader(t *testing.T) {
2371
2381
}
2372
2382
require .Equal (t , expectedLeafHeader , leafHeader )
2373
2383
}
2384
+
2385
+ // TestTxnBufferedWritesOmitAbortSpanChecks verifies that transactions that use
2386
+ // buffered writes do not check the AbortSpan, while still upholding
2387
+ // read-your-own-writes semantics.
2388
+ func TestTxnBufferedWritesOmitAbortSpanChecks (t * testing.T ) {
2389
+ defer leaktest .AfterTest (t )()
2390
+ defer log .Scope (t ).Close (t )
2391
+ ctx := context .Background ()
2392
+
2393
+ var mu struct {
2394
+ syncutil.Mutex
2395
+ txnID uuid.UUID
2396
+ }
2397
+ s := createTestDBWithKnobs (t , & kvserver.StoreTestingKnobs {
2398
+ EvalKnobs : kvserverbase.BatchEvalTestingKnobs {
2399
+ BeforeAbortSpanCheck : func (id uuid.UUID ) {
2400
+ mu .Lock ()
2401
+ defer mu .Unlock ()
2402
+
2403
+ if mu .txnID == id {
2404
+ t .Fatal ("transactions using buffered writes should not check the AbortSpan" )
2405
+ }
2406
+ },
2407
+ },
2408
+ })
2409
+ defer s .Stop ()
2410
+
2411
+ value1 := []byte ("value1" )
2412
+ valueConflict := []byte ("conflict" )
2413
+
2414
+ keyA := []byte ("keyA" )
2415
+
2416
+ txn := kv .NewTxn (ctx , s .DB , 0 /* gatewayNodeID */ )
2417
+ txn .SetBufferedWritesEnabled (true )
2418
+ mu .Lock ()
2419
+ mu .txnID = txn .ID ()
2420
+ mu .Unlock ()
2421
+
2422
+ // Fix the transaction's commit timestamp.
2423
+ _ , err := txn .CommitTimestamp ()
2424
+ require .NoError (t , err )
2425
+
2426
+ // Put transactional value at keyA.
2427
+ require .NoError (t , txn .Put (ctx , keyA , value1 ))
2428
+
2429
+ // Read what we just wrote.
2430
+ b := txn .NewBatch ()
2431
+ b .Get (keyA )
2432
+ require .NoError (t , txn .Run (ctx , b ))
2433
+ expected := map [string ][]byte {
2434
+ "keyA" : value1 ,
2435
+ }
2436
+ checkGetResults (t , expected , b .Results ... )
2437
+
2438
+ // Start another transaction that writes to keyA. This prevents us from
2439
+ // committing at our original timestamp. Moreover, had we not been buffering
2440
+ // our writes, this transaction would have resulted in aborting us and
2441
+ // removing our intent.
2442
+ err = s .DB .Txn (context .Background (), func (ctx context.Context , txn * kv.Txn ) error {
2443
+ require .NoError (t , txn .SetUserPriority (roachpb .MaxUserPriority ))
2444
+ return txn .Put (ctx , keyA , valueConflict )
2445
+ })
2446
+ require .NoError (t , err )
2447
+
2448
+ // Perform another read again. We should still see our previous write, not what
2449
+ // the conflicting transaction wrote.
2450
+ b = txn .NewBatch ()
2451
+ b .Get (keyA )
2452
+ require .NoError (t , txn .Run (ctx , b ))
2453
+ expected = map [string ][]byte {
2454
+ "keyA" : value1 ,
2455
+ }
2456
+ checkGetResults (t , expected , b .Results ... )
2457
+
2458
+ // Try to commit the transaction. We should encounter a WriteTooOldError.
2459
+ err = txn .Commit (ctx )
2460
+ require .Error (t , err )
2461
+ require .Regexp (t , "TransactionRetryWithProtoRefreshError: .*WriteTooOldError" , err )
2462
+ }
0 commit comments