@@ -8,6 +8,7 @@ package integration
8
8
9
9
import (
10
10
"context"
11
+ "errors"
11
12
"fmt"
12
13
"net"
13
14
"os"
@@ -19,6 +20,7 @@ import (
19
20
"go.mongodb.org/mongo-driver/bson"
20
21
"go.mongodb.org/mongo-driver/bson/bsoncodec"
21
22
"go.mongodb.org/mongo-driver/bson/bsonrw"
23
+ "go.mongodb.org/mongo-driver/bson/bsontype"
22
24
"go.mongodb.org/mongo-driver/bson/primitive"
23
25
"go.mongodb.org/mongo-driver/event"
24
26
"go.mongodb.org/mongo-driver/internal/assert"
@@ -1006,3 +1008,137 @@ func TestClientStress(t *testing.T) {
1006
1008
}
1007
1009
})
1008
1010
}
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