@@ -28,13 +28,12 @@ func TestSDAMErrorHandling(t *testing.T) {
28
28
return options .Client ().
29
29
ApplyURI (mtest .ClusterURI ()).
30
30
SetRetryWrites (false ).
31
- SetPoolMonitor (poolMonitor ).
32
31
SetWriteConcern (mtest .MajorityWc )
33
32
}
34
33
baseMtOpts := func () * mtest.Options {
35
34
mtOpts := mtest .NewOptions ().
36
- Topologies (mtest .ReplicaSet ). // Don't run on sharded clusters to avoid complexity of sharded failpoints.
37
- MinServerVersion ("4.0" ). // 4.0+ is required to use failpoints on replica sets.
35
+ Topologies (mtest .ReplicaSet , mtest . Single ). // Don't run on sharded clusters to avoid complexity of sharded failpoints.
36
+ MinServerVersion ("4.0" ). // 4.0+ is required to use failpoints on replica sets.
38
37
ClientOptions (baseClientOpts ())
39
38
40
39
if mtest .ClusterTopologyKind () == mtest .Sharded {
@@ -48,13 +47,14 @@ func TestSDAMErrorHandling(t *testing.T) {
48
47
// blockConnection and appName.
49
48
mt .RunOpts ("before handshake completes" , baseMtOpts ().Auth (true ).MinServerVersion ("4.4" ), func (mt * mtest.T ) {
50
49
mt .RunOpts ("network errors" , noClientOpts , func (mt * mtest.T ) {
51
- mt .Run ("pool cleared on network timeout" , func (mt * mtest.T ) {
52
- // Assert that the pool is cleared when a connection created by an application operation thread
53
- // encounters a network timeout during handshaking. Unlike the non-timeout test below, we only test
54
- // connections created in the foreground for timeouts because connections created by the pool
55
- // maintenance routine can't be timed out using a context.
56
-
57
- appName := "authNetworkTimeoutTest"
50
+ mt .Run ("pool not cleared on operation-scoped network timeout" , func (mt * mtest.T ) {
51
+ // Assert that the pool is not cleared when a connection created by an application
52
+ // operation thread encounters an operation timeout during handshaking. Unlike the
53
+ // non-timeout test below, we only test connections created in the foreground for
54
+ // timeouts because connections created by the pool maintenance routine can't be
55
+ // timed out using a context.
56
+
57
+ appName := "authOperationTimeoutTest"
58
58
// Set failpoint on saslContinue instead of saslStart because saslStart isn't done when using
59
59
// speculative auth.
60
60
mt .SetFailPoint (mtest.FailPoint {
@@ -70,24 +70,61 @@ func TestSDAMErrorHandling(t *testing.T) {
70
70
},
71
71
})
72
72
73
- // Reset the client with the appName specified in the failpoint.
74
- clientOpts := options .Client ().
75
- SetAppName (appName ).
76
- SetRetryWrites (false ).
77
- SetPoolMonitor (poolMonitor )
78
- mt .ResetClient (clientOpts )
79
- clearPoolChan ()
73
+ // Reset the client with the appName specified in the failpoint and the pool monitor.
74
+ tpm := newTestPoolMonitor ()
75
+ mt .ResetClient (baseClientOpts ().SetAppName (appName ).SetPoolMonitor (tpm .PoolMonitor ))
80
76
81
- // The saslContinue blocks for 150ms so run the InsertOne with a 100ms context to cause a network
82
- // timeout during auth and assert that the pool was cleared.
77
+ // Use a context with a 100ms timeout so that the saslContinue delay of 150ms causes
78
+ // an operation-scoped context timeout (i.e. a timeout not caused by a client timeout
79
+ // like connectTimeoutMS or socketTimeoutMS).
83
80
timeoutCtx , cancel := context .WithTimeout (mtest .Background , 100 * time .Millisecond )
84
81
defer cancel ()
85
82
_ , err := mt .Coll .InsertOne (timeoutCtx , bson.D {{"test" , 1 }})
86
83
assert .NotNil (mt , err , "expected InsertOne error, got nil" )
87
84
assert .True (mt , mongo .IsTimeout (err ), "expected timeout error, got %v" , err )
88
85
assert .True (mt , mongo .IsNetworkError (err ), "expected network error, got %v" , err )
89
- assert .True (mt , isPoolCleared (), "expected pool to be cleared but was not " )
86
+ assert .False (mt , tpm . IsPoolCleared (), "expected pool not to be cleared but was cleared " )
90
87
})
88
+
89
+ mt .Run ("pool cleared on non-operation-scoped network timeout" , func (mt * mtest.T ) {
90
+ // Assert that the pool is cleared when a connection created by an application
91
+ // operation thread encounters a timeout caused by connectTimeoutMS during
92
+ // handshaking.
93
+
94
+ appName := "authConnectTimeoutTest"
95
+ // Set failpoint on saslContinue instead of saslStart because saslStart isn't done when using
96
+ // speculative auth.
97
+ mt .SetFailPoint (mtest.FailPoint {
98
+ ConfigureFailPoint : "failCommand" ,
99
+ Mode : mtest.FailPointMode {
100
+ Times : 1 ,
101
+ },
102
+ Data : mtest.FailPointData {
103
+ FailCommands : []string {"saslContinue" },
104
+ BlockConnection : true ,
105
+ BlockTimeMS : 150 ,
106
+ AppName : appName ,
107
+ },
108
+ })
109
+
110
+ // Reset the client with the appName specified in the failpoint and the pool monitor.
111
+ tpm := newTestPoolMonitor ()
112
+ mt .ResetClient (baseClientOpts ().
113
+ SetAppName (appName ).
114
+ SetPoolMonitor (tpm .PoolMonitor ).
115
+ // Set a 100ms socket timeout so that the saslContinue delay of 150ms causes a
116
+ // timeout during socket read (i.e. a timeout not caused by the InsertOne context).
117
+ SetSocketTimeout (100 * time .Millisecond ))
118
+
119
+ // Use context.Background() so that the new connection will not time out due to an
120
+ // operation-scoped timeout.
121
+ _ , err := mt .Coll .InsertOne (context .Background (), bson.D {{"test" , 1 }})
122
+ assert .NotNil (mt , err , "expected InsertOne error, got nil" )
123
+ assert .True (mt , mongo .IsTimeout (err ), "expected timeout error, got %v" , err )
124
+ assert .True (mt , mongo .IsNetworkError (err ), "expected network error, got %v" , err )
125
+ assert .True (mt , tpm .IsPoolCleared (), "expected pool to be cleared but was not" )
126
+ })
127
+
91
128
mt .RunOpts ("pool cleared on non-timeout network error" , noClientOpts , func (mt * mtest.T ) {
92
129
mt .Run ("background" , func (mt * mtest.T ) {
93
130
// Assert that the pool is cleared when a connection created by the background pool maintenance
@@ -106,16 +143,19 @@ func TestSDAMErrorHandling(t *testing.T) {
106
143
},
107
144
})
108
145
109
- clientOpts := options .Client ().
146
+ // Reset the client with the appName specified in the failpoint.
147
+ tpm := newTestPoolMonitor ()
148
+ mt .ResetClient (baseClientOpts ().
110
149
SetAppName (appName ).
111
- SetMinPoolSize (5 ).
112
- SetPoolMonitor (poolMonitor )
113
- mt .ResetClient (clientOpts )
114
- clearPoolChan ()
150
+ SetPoolMonitor (tpm .PoolMonitor ).
151
+ // Set minPoolSize to enable the background pool maintenance goroutine.
152
+ SetMinPoolSize (5 ))
115
153
116
154
time .Sleep (200 * time .Millisecond )
117
- assert .True (mt , isPoolCleared (), "expected pool to be cleared but was not" )
155
+
156
+ assert .True (mt , tpm .IsPoolCleared (), "expected pool to be cleared but was not" )
118
157
})
158
+
119
159
mt .Run ("foreground" , func (mt * mtest.T ) {
120
160
// Assert that the pool is cleared when a connection created by an application thread connection
121
161
// checkout encounters a non-timeout network error during handshaking.
@@ -133,24 +173,23 @@ func TestSDAMErrorHandling(t *testing.T) {
133
173
},
134
174
})
135
175
136
- clientOpts := options .Client ().
137
- SetAppName (appName ).
138
- SetPoolMonitor (poolMonitor )
139
- mt .ResetClient (clientOpts )
140
- clearPoolChan ()
176
+ // Reset the client with the appName specified in the failpoint.
177
+ tpm := newTestPoolMonitor ()
178
+ mt .ResetClient (baseClientOpts ().SetAppName (appName ).SetPoolMonitor (tpm .PoolMonitor ))
141
179
142
180
_ , err := mt .Coll .InsertOne (mtest .Background , bson.D {{"x" , 1 }})
143
181
assert .NotNil (mt , err , "expected InsertOne error, got nil" )
144
182
assert .False (mt , mongo .IsTimeout (err ), "expected non-timeout error, got %v" , err )
145
- assert .True (mt , isPoolCleared (), "expected pool to be cleared but was not" )
183
+ assert .True (mt , tpm . IsPoolCleared (), "expected pool to be cleared but was not" )
146
184
})
147
185
})
148
186
})
149
187
})
150
188
mt .RunOpts ("after handshake completes" , baseMtOpts (), func (mt * mtest.T ) {
151
189
mt .RunOpts ("network errors" , noClientOpts , func (mt * mtest.T ) {
152
190
mt .Run ("pool cleared on non-timeout network error" , func (mt * mtest.T ) {
153
- clearPoolChan ()
191
+ appName := "afterHandshakeNetworkError"
192
+
154
193
mt .SetFailPoint (mtest.FailPoint {
155
194
ConfigureFailPoint : "failCommand" ,
156
195
Mode : mtest.FailPointMode {
@@ -159,16 +198,22 @@ func TestSDAMErrorHandling(t *testing.T) {
159
198
Data : mtest.FailPointData {
160
199
FailCommands : []string {"insert" },
161
200
CloseConnection : true ,
201
+ AppName : appName ,
162
202
},
163
203
})
164
204
205
+ // Reset the client with the appName specified in the failpoint.
206
+ tpm := newTestPoolMonitor ()
207
+ mt .ResetClient (baseClientOpts ().SetAppName (appName ).SetPoolMonitor (tpm .PoolMonitor ))
208
+
165
209
_ , err := mt .Coll .InsertOne (mtest .Background , bson.D {{"test" , 1 }})
166
210
assert .NotNil (mt , err , "expected InsertOne error, got nil" )
167
211
assert .False (mt , mongo .IsTimeout (err ), "expected non-timeout error, got %v" , err )
168
- assert .True (mt , isPoolCleared (), "expected pool to be cleared but was not" )
212
+ assert .True (mt , tpm . IsPoolCleared (), "expected pool to be cleared but was not" )
169
213
})
170
214
mt .Run ("pool not cleared on timeout network error" , func (mt * mtest.T ) {
171
- clearPoolChan ()
215
+ tpm := newTestPoolMonitor ()
216
+ mt .ResetClient (baseClientOpts ().SetPoolMonitor (tpm .PoolMonitor ))
172
217
173
218
_ , err := mt .Coll .InsertOne (mtest .Background , bson.D {{"x" , 1 }})
174
219
assert .Nil (mt , err , "InsertOne error: %v" , err )
@@ -181,11 +226,11 @@ func TestSDAMErrorHandling(t *testing.T) {
181
226
_ , err = mt .Coll .Find (timeoutCtx , filter )
182
227
assert .NotNil (mt , err , "expected Find error, got %v" , err )
183
228
assert .True (mt , mongo .IsTimeout (err ), "expected timeout error, got %v" , err )
184
-
185
- assert .False (mt , isPoolCleared (), "expected pool to not be cleared but was" )
229
+ assert .False (mt , tpm .IsPoolCleared (), "expected pool to not be cleared but was" )
186
230
})
187
231
mt .Run ("pool not cleared on context cancellation" , func (mt * mtest.T ) {
188
- clearPoolChan ()
232
+ tpm := newTestPoolMonitor ()
233
+ mt .ResetClient (baseClientOpts ().SetPoolMonitor (tpm .PoolMonitor ))
189
234
190
235
_ , err := mt .Coll .InsertOne (mtest .Background , bson.D {{"x" , 1 }})
191
236
assert .Nil (mt , err , "InsertOne error: %v" , err )
@@ -204,8 +249,7 @@ func TestSDAMErrorHandling(t *testing.T) {
204
249
assert .False (mt , mongo .IsTimeout (err ), "expected non-timeout error, got %v" , err )
205
250
assert .True (mt , mongo .IsNetworkError (err ), "expected network error, got %v" , err )
206
251
assert .True (mt , errors .Is (err , context .Canceled ), "expected error %v to be context.Canceled" , err )
207
-
208
- assert .False (mt , isPoolCleared (), "expected pool to not be cleared but was" )
252
+ assert .False (mt , tpm .IsPoolCleared (), "expected pool to not be cleared but was" )
209
253
})
210
254
})
211
255
mt .RunOpts ("server errors" , noClientOpts , func (mt * mtest.T ) {
@@ -242,28 +286,32 @@ func TestSDAMErrorHandling(t *testing.T) {
242
286
}
243
287
for _ , tc := range testCases {
244
288
mt .RunOpts (fmt .Sprintf ("command error - %s" , tc .name ), serverErrorsMtOpts , func (mt * mtest.T ) {
245
- clearPoolChan ( )
289
+ appName := fmt . Sprintf ( "command_error_%s" , tc . name )
246
290
247
291
// Cause the next insert to fail with an ok:0 response.
248
- fp := mtest.FailPoint {
292
+ mt . SetFailPoint ( mtest.FailPoint {
249
293
ConfigureFailPoint : "failCommand" ,
250
294
Mode : mtest.FailPointMode {
251
295
Times : 1 ,
252
296
},
253
297
Data : mtest.FailPointData {
254
298
FailCommands : []string {"insert" },
255
299
ErrorCode : tc .errorCode ,
300
+ AppName : appName ,
256
301
},
257
- }
258
- mt .SetFailPoint (fp )
302
+ })
303
+
304
+ // Reset the client with the appName specified in the failpoint.
305
+ tpm := newTestPoolMonitor ()
306
+ mt .ResetClient (baseClientOpts ().SetAppName (appName ).SetPoolMonitor (tpm .PoolMonitor ))
259
307
260
- runServerErrorsTest (mt , tc .isShutdownError )
308
+ runServerErrorsTest (mt , tc .isShutdownError , tpm )
261
309
})
262
310
mt .RunOpts (fmt .Sprintf ("write concern error - %s" , tc .name ), serverErrorsMtOpts , func (mt * mtest.T ) {
263
- clearPoolChan ( )
311
+ appName := fmt . Sprintf ( "write_concern_error_%s" , tc . name )
264
312
265
313
// Cause the next insert to fail with a write concern error.
266
- fp := mtest.FailPoint {
314
+ mt . SetFailPoint ( mtest.FailPoint {
267
315
ConfigureFailPoint : "failCommand" ,
268
316
Mode : mtest.FailPointMode {
269
317
Times : 1 ,
@@ -273,32 +321,36 @@ func TestSDAMErrorHandling(t *testing.T) {
273
321
WriteConcernError : & mtest.WriteConcernErrorData {
274
322
Code : tc .errorCode ,
275
323
},
324
+ AppName : appName ,
276
325
},
277
- }
278
- mt .SetFailPoint (fp )
326
+ })
327
+
328
+ // Reset the client with the appName specified in the failpoint.
329
+ tpm := newTestPoolMonitor ()
330
+ mt .ResetClient (baseClientOpts ().SetAppName (appName ).SetPoolMonitor (tpm .PoolMonitor ))
279
331
280
- runServerErrorsTest (mt , tc .isShutdownError )
332
+ runServerErrorsTest (mt , tc .isShutdownError , tpm )
281
333
})
282
334
}
283
335
})
284
336
})
285
337
}
286
338
287
- func runServerErrorsTest (mt * mtest.T , isShutdownError bool ) {
339
+ func runServerErrorsTest (mt * mtest.T , isShutdownError bool , tpm * testPoolMonitor ) {
288
340
mt .Helper ()
289
341
290
342
_ , err := mt .Coll .InsertOne (mtest .Background , bson.D {{"x" , 1 }})
291
343
assert .NotNil (mt , err , "expected InsertOne error, got nil" )
292
344
293
345
// The pool should always be cleared for shutdown errors, regardless of server version.
294
346
if isShutdownError {
295
- assert .True (mt , isPoolCleared (), "expected pool to be cleared, but was not" )
347
+ assert .True (mt , tpm . IsPoolCleared (), "expected pool to be cleared, but was not" )
296
348
return
297
349
}
298
350
299
351
// For non-shutdown errors, the pool is only cleared if the error is from a pre-4.2 server.
300
352
wantCleared := mtest .CompareServerVersions (mtest .ServerVersion (), "4.2" ) < 0
301
- gotCleared := isPoolCleared ()
302
- assert .Equal (mt , wantCleared , gotCleared , "expected pool to be cleared: %v ; pool was cleared: %v " ,
353
+ gotCleared := tpm . IsPoolCleared ()
354
+ assert .Equal (mt , wantCleared , gotCleared , "expected pool to be cleared: %t ; pool was cleared: %t " ,
303
355
wantCleared , gotCleared )
304
356
}
0 commit comments