@@ -13,6 +13,7 @@ import (
13
13
"os"
14
14
"path"
15
15
"reflect"
16
+ "strconv"
16
17
"testing"
17
18
"time"
18
19
@@ -27,7 +28,13 @@ import (
27
28
"go.mongodb.org/mongo-driver/mongo/writeconcern"
28
29
"go.mongodb.org/mongo-driver/tag"
29
30
"go.mongodb.org/mongo-driver/x/bsonx"
31
+ "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
32
+ "go.mongodb.org/mongo-driver/x/mongo/driver"
33
+ "go.mongodb.org/mongo-driver/x/mongo/driver/address"
30
34
"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
35
+ "go.mongodb.org/mongo-driver/x/mongo/driver/description"
36
+ "go.mongodb.org/mongo-driver/x/mongo/driver/drivertest"
37
+ "go.mongodb.org/mongo-driver/x/mongo/driver/operation"
31
38
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
32
39
"go.mongodb.org/mongo-driver/x/mongo/driver/uuid"
33
40
)
@@ -283,6 +290,161 @@ func TestClient_ReplaceTopologyError(t *testing.T) {
283
290
284
291
}
285
292
293
+ type retryableSSD struct {
294
+ C driver.Connection
295
+ }
296
+
297
+ var _ driver.Deployment = retryableSSD {}
298
+ var _ driver.Server = retryableSSD {}
299
+
300
+ func (rssd retryableSSD ) SelectServer (context.Context , description.ServerSelector ) (driver.Server , error ) {
301
+ return rssd , nil
302
+ }
303
+
304
+ func (rssd retryableSSD ) Kind () description.TopologyKind {
305
+ return description .Single
306
+ }
307
+
308
+ func (rssd retryableSSD ) Connection (context.Context ) (driver.Connection , error ) {
309
+ return rssd .C , nil
310
+ }
311
+
312
+ func (rssd retryableSSD ) SupportsRetryWrites () bool {
313
+ return true
314
+ }
315
+
316
+ func TestRetryWritesError20Wrapped (t * testing.T ) {
317
+ serverVersion , err := getServerVersion (createTestDatabase (t , nil ))
318
+ require .NoError (t , err )
319
+
320
+ if compareVersions (t , serverVersion , "3.6" ) < 0 {
321
+ t .Skip ()
322
+ }
323
+
324
+ idx , writeError := bsoncore .AppendDocumentStart (nil )
325
+ writeError = bsoncore .AppendInt32Element (writeError , "ok" , 1 )
326
+ elemIdx , elem := bsoncore .AppendDocumentStart (nil )
327
+ elem = bsoncore .AppendInt32Element (elem , "index" , 0 )
328
+ elem = bsoncore .AppendStringElement (elem , "errmsg" , "Transaction numbers" )
329
+ elem = bsoncore .AppendInt32Element (elem , "code" , 20 )
330
+ elem , _ = bsoncore .AppendDocumentEnd (elem , elemIdx )
331
+ writeErrorsIdx , writeErrors := bsoncore .AppendArrayStart (nil )
332
+ writeErrors = bsoncore .AppendDocumentElement (writeErrors , strconv .Itoa (0 ), elem )
333
+ writeErrors , _ = bsoncore .AppendArrayEnd (writeErrors , writeErrorsIdx )
334
+ writeError = bsoncore .AppendArrayElement (writeError , "writeErrors" , writeErrors )
335
+ writeError , _ = bsoncore .AppendDocumentEnd (writeError , idx )
336
+
337
+ idx , writeErrorNot20 := bsoncore .AppendDocumentStart (nil )
338
+ writeErrorNot20 = bsoncore .AppendInt32Element (writeErrorNot20 , "ok" , 1 )
339
+ elemIdx , elem = bsoncore .AppendDocumentStart (nil )
340
+ elem = bsoncore .AppendInt32Element (elem , "index" , 0 )
341
+ elem = bsoncore .AppendStringElement (elem , "errmsg" , "Transaction numbers" )
342
+ elem = bsoncore .AppendInt32Element (elem , "code" , 19 )
343
+ elem , _ = bsoncore .AppendDocumentEnd (elem , elemIdx )
344
+ writeErrorsIdx , writeErrors = bsoncore .AppendArrayStart (nil )
345
+ writeErrors = bsoncore .AppendDocumentElement (writeErrors , strconv .Itoa (0 ), elem )
346
+ writeErrors , _ = bsoncore .AppendArrayEnd (writeErrors , writeErrorsIdx )
347
+ writeErrorNot20 = bsoncore .AppendArrayElement (writeErrorNot20 , "writeErrors" , writeErrors )
348
+ writeErrorNot20 , _ = bsoncore .AppendDocumentEnd (writeErrorNot20 , idx )
349
+
350
+ idx , writeErrorOnly20 := bsoncore .AppendDocumentStart (nil )
351
+ writeErrorOnly20 = bsoncore .AppendInt32Element (writeErrorOnly20 , "ok" , 1 )
352
+ elemIdx , elem = bsoncore .AppendDocumentStart (nil )
353
+ elem = bsoncore .AppendInt32Element (elem , "index" , 0 )
354
+ elem = bsoncore .AppendStringElement (elem , "errmsg" , "something other than transaction numbers" )
355
+ elem = bsoncore .AppendInt32Element (elem , "code" , 20 )
356
+ elem , _ = bsoncore .AppendDocumentEnd (elem , elemIdx )
357
+ writeErrorsIdx , writeErrors = bsoncore .AppendArrayStart (nil )
358
+ writeErrors = bsoncore .AppendDocumentElement (writeErrors , strconv .Itoa (0 ), elem )
359
+ writeErrors , _ = bsoncore .AppendArrayEnd (writeErrors , writeErrorsIdx )
360
+ writeErrorOnly20 = bsoncore .AppendArrayElement (writeErrorOnly20 , "writeErrors" , writeErrors )
361
+ writeErrorOnly20 , _ = bsoncore .AppendDocumentEnd (writeErrorOnly20 , idx )
362
+
363
+ idx , notOk := bsoncore .AppendDocumentStart (nil )
364
+ notOk = bsoncore .AppendInt64Element (notOk , "ok" , 0 )
365
+ notOk = bsoncore .AppendStringElement (notOk , "errmsg" , "Transaction numbers" )
366
+ notOk = bsoncore .AppendInt32Element (notOk , "code" , 20 )
367
+ notOk , _ = bsoncore .AppendDocumentEnd (notOk , idx )
368
+
369
+ idx , not20notOK := bsoncore .AppendDocumentStart (nil )
370
+ not20notOK = bsoncore .AppendInt64Element (not20notOK , "ok" , 0 )
371
+ not20notOK = bsoncore .AppendStringElement (not20notOK , "errmsg" , "Transaction numbers" )
372
+ not20notOK = bsoncore .AppendInt32Element (not20notOK , "code" , 19 )
373
+ not20notOK , _ = bsoncore .AppendDocumentEnd (not20notOK , idx )
374
+
375
+ idx , only20NotOK := bsoncore .AppendDocumentStart (nil )
376
+ only20NotOK = bsoncore .AppendInt64Element (only20NotOK , "ok" , 0 )
377
+ only20NotOK = bsoncore .AppendStringElement (only20NotOK , "errmsg" , "something other than transaction numbers" )
378
+ only20NotOK = bsoncore .AppendInt32Element (only20NotOK , "code" , 20 )
379
+ only20NotOK , _ = bsoncore .AppendDocumentEnd (only20NotOK , idx )
380
+
381
+ tests := []struct {
382
+ name string
383
+ wireMessage []byte // bsoncore byte slice
384
+ shouldError bool
385
+ expectedErrorMessage string
386
+ }{
387
+ {"writeError" , writeError , true , driver .ErrUnsupportedStorageEngine .Error ()},
388
+ {"writeError with only err code 20 and wrong err message" , writeErrorOnly20 , true , "write command error: [{write errors: [{something other than transaction numbers}]}, {<nil>}]" },
389
+ {"writeError with only err code 19 and right err message" , writeErrorNot20 , true , "write command error: [{write errors: [{Transaction numbers}]}, {<nil>}]" },
390
+ {"NotOkError" , notOk , true , driver .ErrUnsupportedStorageEngine .Error ()},
391
+ {"NotOkError with err code 20 and wrong err message" , only20NotOK , true , "something other than transaction numbers" },
392
+ {"NotOkError with err code 19 and right err message" , not20notOK , true , "Transaction numbers" },
393
+ }
394
+
395
+ for _ , test := range tests {
396
+ t .Run (test .name , func (t * testing.T ) {
397
+ conn := & drivertest.ChannelConn {
398
+ Written : make (chan []byte , 1 ),
399
+ Desc : description.Server {
400
+ CanonicalAddr : address .Address ("localhost:27017" ),
401
+ MaxDocumentSize : 16777216 ,
402
+ MaxMessageSize : 48000000 ,
403
+ MaxBatchCount : 100000 ,
404
+ SessionTimeoutMinutes : 30 ,
405
+ Kind : description .RSPrimary ,
406
+ WireVersion : & description.VersionRange {
407
+ Max : 8 ,
408
+ },
409
+ },
410
+ ReadResp : make (chan []byte , 1 ),
411
+ }
412
+
413
+ conn .ReadResp <- drivertest .MakeReply (test .wireMessage )
414
+
415
+ deployment := retryableSSD {C : conn }
416
+
417
+ client := createTestClient (t )
418
+ coll := client .Database ("test" ).Collection ("test" )
419
+
420
+ sess , err := client .StartSession ()
421
+ defer sess .EndSession (context .Background ())
422
+ noerr (t , err )
423
+
424
+ idx , writeError = bsoncore .AppendDocumentStart (nil )
425
+ writeError = bsoncore .AppendStringElement (writeError , "_id" , "1" )
426
+ writeError , _ = bsoncore .AppendDocumentEnd (writeError , idx )
427
+
428
+ op := operation .NewInsert (writeError ).CommandMonitor (coll .client .monitor ).ClusterClock (coll .client .clock ).
429
+ Database (coll .db .name ).Collection (coll .name ).
430
+ Deployment (coll .client .topology ).Deployment (deployment ).Retry (driver .RetryOnce ).Session (sess .(* sessionImpl ).clientSession )
431
+
432
+ err = op .Execute (context .Background ())
433
+ if test .shouldError {
434
+ if err == nil || err .Error () != test .expectedErrorMessage {
435
+ t .Fatalf ("unexpected error occured, wanted: %v got: %v" , test .expectedErrorMessage , err )
436
+ }
437
+ return
438
+ }
439
+
440
+ if err != nil {
441
+ t .Fatalf ("did not expect an error, instead recieved: %v" , err )
442
+ }
443
+
444
+ })
445
+ }
446
+ }
447
+
286
448
func TestClient_ListDatabases_noFilter (t * testing.T ) {
287
449
t .Parallel ()
288
450
0 commit comments