@@ -8,6 +8,7 @@ package integration
88
99import (
1010 "context"
11+ "errors"
1112 "fmt"
1213 "net"
1314 "os"
@@ -19,6 +20,7 @@ import (
1920 "go.mongodb.org/mongo-driver/bson"
2021 "go.mongodb.org/mongo-driver/bson/bsoncodec"
2122 "go.mongodb.org/mongo-driver/bson/bsonrw"
23+ "go.mongodb.org/mongo-driver/bson/bsontype"
2224 "go.mongodb.org/mongo-driver/bson/primitive"
2325 "go.mongodb.org/mongo-driver/event"
2426 "go.mongodb.org/mongo-driver/internal/assert"
@@ -1006,3 +1008,137 @@ func TestClientStress(t *testing.T) {
10061008 }
10071009 })
10081010}
1011+
1012+ func TestCSOT (t * testing.T ) {
1013+ mt := mtest .New (t , mtest .NewOptions ().CreateClient (false ))
1014+
1015+ csotOpts := mtest .NewOptions ().ClientOptions (options .Client ().SetTimeout (10 * time .Second ))
1016+ mt .RunOpts ("includes maxTimeMS if CSOT timeout is set" , csotOpts , func (mt * mtest.T ) {
1017+ mt .Run ("with context.Background" , func (mt * mtest.T ) {
1018+ _ , err := mt .Coll .InsertOne (context .Background (), bson.D {})
1019+ require .NoError (mt , err , "InsertOne error" )
1020+
1021+ maxTimeVal := mt .GetStartedEvent ().Command .Lookup ("maxTimeMS" )
1022+
1023+ require .True (mt , len (maxTimeVal .Value ) > 0 , "expected maxTimeMS BSON value to be non-empty" )
1024+ require .Equal (mt , maxTimeVal .Type , bsontype .Int64 , "expected maxTimeMS value to be type Int64" )
1025+
1026+ maxTimeMS := maxTimeVal .Int64 ()
1027+ assert .True (mt , maxTimeMS > 0 , "expected maxTimeMS value to be greater than 0" )
1028+ })
1029+ mt .Run ("with context.WithTimeout" , func (mt * mtest.T ) {
1030+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Minute )
1031+ defer cancel ()
1032+
1033+ _ , err := mt .Coll .InsertOne (ctx , bson.D {})
1034+ require .NoError (mt , err , "InsertOne error" )
1035+
1036+ maxTimeVal := mt .GetStartedEvent ().Command .Lookup ("maxTimeMS" )
1037+ require .True (mt , len (maxTimeVal .Value ) > 0 , "expected maxTimeMS BSON value to be non-empty" )
1038+ require .Equal (mt , maxTimeVal .Type , bsontype .Int64 , "expected maxTimeMS value to be type Int64" )
1039+
1040+ maxTimeMS := maxTimeVal .Int64 ()
1041+ assert .True (mt ,
1042+ maxTimeMS > 60_000 ,
1043+ "expected maxTimeMS value to be greater than 60000, but got %v" ,
1044+ maxTimeMS )
1045+ })
1046+ })
1047+
1048+ mt .RunOpts ("timeout errors wrap context.DeadlineExceeded" , csotOpts , func (mt * mtest.T ) {
1049+ // Test that a client-side timeout is a context.DeadlineExceeded
1050+ mt .Run ("MaxTimeMSExceeded" , func (mt * mtest.T ) {
1051+ _ , err := mt .Coll .InsertOne (context .Background (), bson.D {})
1052+ require .NoError (mt , err , "InsertOne error" )
1053+
1054+ mt .SetFailPoint (mtest.FailPoint {
1055+ ConfigureFailPoint : "failCommand" ,
1056+ Mode : mtest.FailPointMode {
1057+ Times : 1 ,
1058+ },
1059+ Data : mtest.FailPointData {
1060+ FailCommands : []string {"find" },
1061+ ErrorCode : 50 , // MaxTimeMSExceeded
1062+ },
1063+ })
1064+
1065+ err = mt .Coll .FindOne (context .Background (), bson.D {}).Err ()
1066+
1067+ assert .True (mt ,
1068+ errors .Is (err , context .DeadlineExceeded ),
1069+ "expected error %[1]T(%[1]q) to wrap context.DeadlineExceeded" ,
1070+ err )
1071+ assert .True (mt ,
1072+ mongo .IsTimeout (err ),
1073+ "expected error %[1]T(%[1]q) to be a timeout error" ,
1074+ err )
1075+ })
1076+ // Test that a server-side timeout is a context.DeadlineExceeded
1077+ mt .Run ("ErrDeadlineWouldBeExceeded" , func (mt * mtest.T ) {
1078+ _ , err := mt .Coll .InsertOne (context .Background (), bson.D {})
1079+ require .NoError (mt , err , "InsertOne error" )
1080+
1081+ mt .SetFailPoint (mtest.FailPoint {
1082+ ConfigureFailPoint : "failCommand" ,
1083+ Mode : mtest.FailPointMode {
1084+ Times : 1 ,
1085+ },
1086+ Data : mtest.FailPointData {
1087+ FailCommands : []string {"find" },
1088+ BlockConnection : true ,
1089+ BlockTimeMS : 1000 ,
1090+ },
1091+ })
1092+
1093+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Microsecond )
1094+ defer cancel ()
1095+ err = mt .Coll .FindOne (ctx , bson.D {}).Err ()
1096+
1097+ assert .True (mt ,
1098+ errors .Is (err , driver .ErrDeadlineWouldBeExceeded ),
1099+ "expected error %[1]T(%[1]q) to wrap driver.ErrDeadlineWouldBeExceeded" ,
1100+ err )
1101+ assert .True (mt ,
1102+ errors .Is (err , context .DeadlineExceeded ),
1103+ "expected error %[1]T(%[1]q) to wrap context.DeadlineExceeded" ,
1104+ err )
1105+ assert .True (mt ,
1106+ mongo .IsTimeout (err ),
1107+ "expected error %[1]T(%[1]q) to be a timeout error" ,
1108+ err )
1109+ })
1110+ mt .Run ("context.DeadlineExceeded" , func (mt * mtest.T ) {
1111+ _ , err := mt .Coll .InsertOne (context .Background (), bson.D {})
1112+ require .NoError (mt , err , "InsertOne error" )
1113+
1114+ mt .SetFailPoint (mtest.FailPoint {
1115+ ConfigureFailPoint : "failCommand" ,
1116+ Mode : mtest.FailPointMode {
1117+ Times : 1 ,
1118+ },
1119+ Data : mtest.FailPointData {
1120+ FailCommands : []string {"find" },
1121+ BlockConnection : true ,
1122+ BlockTimeMS : 1000 ,
1123+ },
1124+ })
1125+
1126+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Millisecond )
1127+ defer cancel ()
1128+ err = mt .Coll .FindOne (ctx , bson.D {}).Err ()
1129+
1130+ assert .False (mt ,
1131+ errors .Is (err , driver .ErrDeadlineWouldBeExceeded ),
1132+ "expected error %[1]T(%[1]q) to not wrap driver.ErrDeadlineWouldBeExceeded" ,
1133+ err )
1134+ assert .True (mt ,
1135+ errors .Is (err , context .DeadlineExceeded ),
1136+ "expected error %[1]T(%[1]q) to wrap context.DeadlineExceeded" ,
1137+ err )
1138+ assert .True (mt ,
1139+ mongo .IsTimeout (err ),
1140+ "expected error %[1]T(%[1]q) to be a timeout error" ,
1141+ err )
1142+ })
1143+ })
1144+ }
0 commit comments