Skip to content

Commit eced6de

Browse files
author
Divjot Arora
committed
GODRIVER-1620 Correctly unwrap network errors for SDAM error handling (#410)
1 parent a651747 commit eced6de

File tree

3 files changed

+77
-22
lines changed

3 files changed

+77
-22
lines changed

mongo/integration/primary_stepdown_test.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -124,24 +124,4 @@ func TestConnectionsSurvivePrimaryStepDown(t *testing.T) {
124124
})
125125
}
126126
})
127-
mt.RunOpts("network errors", mtest.NewOptions().ClientOptions(clientOpts).MinServerVersion("4.0"), func(mt *mtest.T) {
128-
// expect that a server's connection pool will be cleared if a non-timeout network error occurs during an
129-
// operation
130-
131-
clearPoolChan()
132-
mt.SetFailPoint(mtest.FailPoint{
133-
ConfigureFailPoint: "failCommand",
134-
Mode: mtest.FailPointMode{
135-
Times: 1,
136-
},
137-
Data: mtest.FailPointData{
138-
FailCommands: []string{"insert"},
139-
CloseConnection: true,
140-
},
141-
})
142-
143-
_, err := mt.Coll.InsertOne(mtest.Background, bson.D{{"test", 1}})
144-
assert.NotNil(mt, err, "expected InsertOne error, got nil")
145-
assert.True(mt, isPoolCleared(), "expected pool to be cleared but was not")
146-
})
147127
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (C) MongoDB, Inc. 2017-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package integration
8+
9+
import (
10+
"context"
11+
"testing"
12+
"time"
13+
14+
"go.mongodb.org/mongo-driver/bson"
15+
"go.mongodb.org/mongo-driver/internal/testutil/assert"
16+
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
17+
"go.mongodb.org/mongo-driver/mongo/options"
18+
)
19+
20+
func TestSDAMErrorHandling(t *testing.T) {
21+
mt := mtest.New(t, noClientOpts)
22+
clientOpts := options.Client().
23+
ApplyURI(mt.ConnString()).
24+
SetRetryWrites(false).
25+
SetPoolMonitor(poolMonitor).
26+
SetWriteConcern(mtest.MajorityWc)
27+
mtOpts := mtest.NewOptions().
28+
Topologies(mtest.ReplicaSet). // Don't run on sharded clusters to avoid complexity of sharded failpoints.
29+
MinServerVersion("4.0"). // 4.0+ is required to use failpoints on replica sets.
30+
ClientOptions(clientOpts)
31+
32+
mt.RunOpts("network errors", mtOpts, func(mt *mtest.T) {
33+
mt.Run("pool cleared on non-timeout network error", func(mt *mtest.T) {
34+
clearPoolChan()
35+
mt.SetFailPoint(mtest.FailPoint{
36+
ConfigureFailPoint: "failCommand",
37+
Mode: mtest.FailPointMode{
38+
Times: 1,
39+
},
40+
Data: mtest.FailPointData{
41+
FailCommands: []string{"insert"},
42+
CloseConnection: true,
43+
},
44+
})
45+
46+
_, err := mt.Coll.InsertOne(mtest.Background, bson.D{{"test", 1}})
47+
assert.NotNil(mt, err, "expected InsertOne error, got nil")
48+
assert.True(mt, isPoolCleared(), "expected pool to be cleared but was not")
49+
})
50+
mt.Run("pool not cleared on timeout network error", func(mt *mtest.T) {
51+
clearPoolChan()
52+
53+
_, err := mt.Coll.InsertOne(mtest.Background, bson.D{{"x", 1}})
54+
assert.Nil(mt, err, "InsertOne error: %v", err)
55+
56+
filter := bson.M{
57+
"$where": "function() { sleep(1000); return false; }",
58+
}
59+
timeoutCtx, cancel := context.WithTimeout(mtest.Background, 100*time.Millisecond)
60+
defer cancel()
61+
_, err = mt.Coll.Find(timeoutCtx, filter)
62+
assert.NotNil(mt, err, "expected Find error, got %v", err)
63+
64+
assert.False(mt, isPoolCleared(), "expected pool to not be cleared but was")
65+
})
66+
})
67+
}

x/mongo/driver/topology/server.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,14 +634,22 @@ func (ss *ServerSubscription) Unsubscribe() error {
634634

635635
// unwrapConnectionError returns the connection error wrapped by err, or nil if err does not wrap a connection error.
636636
func unwrapConnectionError(err error) error {
637+
// This is essentially an implementation of errors.As to unwrap this error until we get a ConnectionError and then
638+
// return ConnectionError.Wrapped.
639+
637640
connErr, ok := err.(ConnectionError)
638641
if ok {
639642
return connErr.Wrapped
640643
}
641644

642645
driverErr, ok := err.(driver.Error)
643-
if ok && driverErr.NetworkError() {
644-
return driverErr.Wrapped
646+
if !ok || !driverErr.NetworkError() {
647+
return nil
648+
}
649+
650+
connErr, ok = driverErr.Wrapped.(ConnectionError)
651+
if ok {
652+
return connErr.Wrapped
645653
}
646654

647655
return nil

0 commit comments

Comments
 (0)