@@ -768,6 +768,77 @@ func TestClient(t *testing.T) {
768768 "expected 'OP_MSG' OpCode in wire message, got %q" , pair .Sent .OpCode .String ())
769769 }
770770 })
771+
772+ opts := mtest .NewOptions ().
773+ // Blocking failpoints don't work on pre-4.2 and sharded clusters.
774+ Topologies (mtest .Single , mtest .ReplicaSet ).
775+ MinServerVersion ("4.2" ).
776+ // Expliticly enable retryable reads and retryable writes.
777+ ClientOptions (options .Client ().SetRetryReads (true ).SetRetryWrites (true ))
778+ mt .RunOpts ("operations don't retry after a context timeout" , opts , func (mt * mtest.T ) {
779+ testCases := []struct {
780+ desc string
781+ operation func (context.Context , * mongo.Collection ) error
782+ }{
783+ {
784+ desc : "read op" ,
785+ operation : func (ctx context.Context , coll * mongo.Collection ) error {
786+ return coll .FindOne (ctx , bson.D {}).Err ()
787+ },
788+ },
789+ {
790+ desc : "write op" ,
791+ operation : func (ctx context.Context , coll * mongo.Collection ) error {
792+ _ , err := coll .InsertOne (ctx , bson.D {})
793+ return err
794+ },
795+ },
796+ }
797+
798+ for _ , tc := range testCases {
799+ mt .Run (tc .desc , func (mt * mtest.T ) {
800+ _ , err := mt .Coll .InsertOne (context .Background (), bson.D {})
801+ require .NoError (mt , err )
802+
803+ mt .SetFailPoint (mtest.FailPoint {
804+ ConfigureFailPoint : "failCommand" ,
805+ Mode : "alwaysOn" ,
806+ Data : mtest.FailPointData {
807+ FailCommands : []string {"find" , "insert" },
808+ BlockConnection : true ,
809+ BlockTimeMS : 500 ,
810+ },
811+ })
812+
813+ mt .ClearEvents ()
814+
815+ for i := 0 ; i < 50 ; i ++ {
816+ // Run 50 operations, each with a timeout of 50ms. Expect
817+ // them to all return a timeout error because the failpoint
818+ // blocks find operations for 500ms. Run 50 to increase the
819+ // probability that an operation will time out in a way that
820+ // can cause a retry.
821+ ctx , cancel := context .WithTimeout (context .Background (), 50 * time .Millisecond )
822+ err = tc .operation (ctx , mt .Coll )
823+ cancel ()
824+ assert .ErrorIs (mt , err , context .DeadlineExceeded )
825+ assert .True (mt , mongo .IsTimeout (err ), "expected mongo.IsTimeout(err) to be true" )
826+
827+ // Assert that each operation reported exactly one command
828+ // started events, which means the operation did not retry
829+ // after the context timeout.
830+ evts := mt .GetAllStartedEvents ()
831+ require .Len (mt ,
832+ mt .GetAllStartedEvents (),
833+ 1 ,
834+ "expected exactly 1 command started event per operation, but got %d after %d iterations" ,
835+ len (evts ),
836+ i )
837+ mt .ClearEvents ()
838+ }
839+ })
840+ }
841+ })
771842}
772843
773844func TestClient_BSONOptions (t * testing.T ) {
0 commit comments