@@ -417,6 +417,117 @@ func TestConvenientTransactions(t *testing.T) {
417
417
assert .True (t , ok , "expected result type %T, got %T" , false , res )
418
418
assert .False (t , resBool , "expected result false, got %v" , resBool )
419
419
})
420
+ t .Run ("expired context before commitTransaction does not retry" , func (t * testing.T ) {
421
+ withTransactionTimeout = 2 * time .Second
422
+
423
+ coll := db .Collection ("test" )
424
+ // Explicitly create the collection on server because implicit collection creation is not allowed in
425
+ // transactions for server versions <= 4.2.
426
+ err := db .RunCommand (bgCtx , bson.D {{"create" , coll .Name ()}}).Err ()
427
+ assert .Nil (t , err , "error creating collection on server: %v" , err )
428
+ defer func () {
429
+ _ = coll .Drop (bgCtx )
430
+ }()
431
+
432
+ sess , err := client .StartSession ()
433
+ assert .Nil (t , err , "StartSession error: %v" , err )
434
+ defer sess .EndSession (context .Background ())
435
+
436
+ // Create context with short timeout.
437
+ withTransactionContext , cancel := context .WithTimeout (context .Background (), time .Nanosecond )
438
+ defer cancel ()
439
+ callback := func () {
440
+ _ , _ = sess .WithTransaction (withTransactionContext , func (sessCtx SessionContext ) (interface {}, error ) {
441
+ _ , err := coll .InsertOne (sessCtx , bson.D {{}})
442
+ return nil , err
443
+ })
444
+ }
445
+
446
+ // Assert that transaction fails within 500ms and not 2 seconds.
447
+ assert .Soon (t , callback , 500 * time .Millisecond )
448
+ })
449
+ t .Run ("canceled context before commitTransaction does not retry" , func (t * testing.T ) {
450
+ withTransactionTimeout = 2 * time .Second
451
+
452
+ coll := db .Collection ("test" )
453
+ // Explicitly create the collection on server because implicit collection creation is not allowed in
454
+ // transactions for server versions <= 4.2.
455
+ err := db .RunCommand (bgCtx , bson.D {{"create" , coll .Name ()}}).Err ()
456
+ assert .Nil (t , err , "error creating collection on server: %v" , err )
457
+ defer func () {
458
+ _ = coll .Drop (bgCtx )
459
+ }()
460
+
461
+ sess , err := client .StartSession ()
462
+ assert .Nil (t , err , "StartSession error: %v" , err )
463
+ defer sess .EndSession (context .Background ())
464
+
465
+ // Create context and cancel it immediately.
466
+ withTransactionContext , cancel := context .WithTimeout (context .Background (), 2 * time .Second )
467
+ cancel ()
468
+ callback := func () {
469
+ _ , _ = sess .WithTransaction (withTransactionContext , func (sessCtx SessionContext ) (interface {}, error ) {
470
+ _ , err := coll .InsertOne (sessCtx , bson.D {{}})
471
+ return nil , err
472
+ })
473
+ }
474
+
475
+ // Assert that transaction fails within 500ms and not 2 seconds.
476
+ assert .Soon (t , callback , 500 * time .Millisecond )
477
+ })
478
+ t .Run ("slow operation before commitTransaction retries" , func (t * testing.T ) {
479
+ withTransactionTimeout = 2 * time .Second
480
+
481
+ coll := db .Collection ("test" )
482
+ // Explicitly create the collection on server because implicit collection creation is not allowed in
483
+ // transactions for server versions <= 4.2.
484
+ err := db .RunCommand (bgCtx , bson.D {{"create" , coll .Name ()}}).Err ()
485
+ assert .Nil (t , err , "error creating collection on server: %v" , err )
486
+ defer func () {
487
+ _ = coll .Drop (bgCtx )
488
+ }()
489
+
490
+ // Set failpoint to block insertOne once for 500ms.
491
+ failpoint := bson.D {{"configureFailPoint" , "failCommand" },
492
+ {"mode" , bson.D {
493
+ {"times" , 1 },
494
+ }},
495
+ {"data" , bson.D {
496
+ {"failCommands" , bson.A {"insert" }},
497
+ {"blockConnection" , true },
498
+ {"blockTimeMS" , 500 },
499
+ }},
500
+ }
501
+ err = dbAdmin .RunCommand (bgCtx , failpoint ).Err ()
502
+ assert .Nil (t , err , "error setting failpoint: %v" , err )
503
+ defer func () {
504
+ err = dbAdmin .RunCommand (bgCtx , bson.D {
505
+ {"configureFailPoint" , "failCommand" },
506
+ {"mode" , "off" },
507
+ }).Err ()
508
+ assert .Nil (t , err , "error turning off failpoint: %v" , err )
509
+ }()
510
+
511
+ sess , err := client .StartSession ()
512
+ assert .Nil (t , err , "StartSession error: %v" , err )
513
+ defer sess .EndSession (context .Background ())
514
+
515
+ callback := func () {
516
+ _ , err = sess .WithTransaction (context .Background (), func (sessCtx SessionContext ) (interface {}, error ) {
517
+ // Set a timeout of 300ms to cause a timeout on first insertOne
518
+ // and force a retry.
519
+ c , cancel := context .WithTimeout (sessCtx , 300 * time .Millisecond )
520
+ defer cancel ()
521
+
522
+ _ , err := coll .InsertOne (c , bson.D {{}})
523
+ return nil , err
524
+ })
525
+ assert .Nil (t , err , "WithTransaction error: %v" , err )
526
+ }
527
+
528
+ // Assert that transaction passes within 2 seconds.
529
+ assert .Soon (t , callback , 2 * time .Second )
530
+ })
420
531
}
421
532
422
533
func setupConvenientTransactions (t * testing.T , extraClientOpts ... * options.ClientOptions ) * Client {
0 commit comments