Skip to content

Commit a2b9ada

Browse files
authored
GODRIVER-1910 Test that PoolClearedErrors are retryable (#840)
1 parent 29d768e commit a2b9ada

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed

mongo/integration/primary_stepdown_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ func (tpm *testPoolMonitor) Events(filters ...func(*event.PoolEvent) bool) []*ev
7272
return filtered
7373
}
7474

75+
// ClearEvents will reset the events collected by the testPoolMonitor.
76+
func (tpm *testPoolMonitor) ClearEvents() {
77+
tpm.mu.Lock()
78+
defer tpm.mu.Unlock()
79+
tpm.events = tpm.events[:0]
80+
}
81+
7582
// IsPoolCleared returns true if there are any events of type "event.PoolCleared" in the events
7683
// recorded by the testPoolMonitor.
7784
func (tpm *testPoolMonitor) IsPoolCleared() bool {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (C) MongoDB, Inc. 2022-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+
"sync"
12+
"testing"
13+
"time"
14+
15+
"github.com/stretchr/testify/assert"
16+
"go.mongodb.org/mongo-driver/bson"
17+
"go.mongodb.org/mongo-driver/event"
18+
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
19+
"go.mongodb.org/mongo-driver/mongo/options"
20+
)
21+
22+
func TestRetryableReadsProse(t *testing.T) {
23+
tpm := newTestPoolMonitor()
24+
25+
// Client options with MaxPoolSize of 1 and RetryReads used per the test description.
26+
// Lower HeartbeatInterval used to speed the test up for any server that uses streaming
27+
// heartbeats. Only connect to first host in list for sharded clusters.
28+
hosts := mtest.ClusterConnString().Hosts
29+
clientOpts := options.Client().SetMaxPoolSize(1).SetRetryReads(true).
30+
SetPoolMonitor(tpm.PoolMonitor).SetHeartbeatInterval(500 * time.Millisecond).
31+
SetHosts(hosts[:1])
32+
33+
mtOpts := mtest.NewOptions().ClientOptions(clientOpts).MinServerVersion("4.3")
34+
mt := mtest.New(t, mtOpts)
35+
defer mt.Close()
36+
37+
mt.Run("PoolClearedError retryability", func(mt *mtest.T) {
38+
// Insert a document to test collection.
39+
_, err := mt.Coll.InsertOne(context.Background(), bson.D{{"x", 1}})
40+
assert.Nil(mt, err, "InsertOne error: %v", err)
41+
42+
// Force Find to block for 1 second once.
43+
mt.SetFailPoint(mtest.FailPoint{
44+
ConfigureFailPoint: "failCommand",
45+
Mode: mtest.FailPointMode{
46+
Times: 1,
47+
},
48+
Data: mtest.FailPointData{
49+
FailCommands: []string{"find"},
50+
ErrorCode: 91,
51+
BlockConnection: true,
52+
BlockTimeMS: 1000,
53+
},
54+
})
55+
56+
// Clear CMAP and command events.
57+
tpm.ClearEvents()
58+
mt.ClearEvents()
59+
60+
// Perform a FindOne on two different threads and assert both operations are
61+
// successful.
62+
var wg sync.WaitGroup
63+
for i := 0; i < 2; i++ {
64+
wg.Add(1)
65+
go func() {
66+
defer wg.Done()
67+
res := mt.Coll.FindOne(context.Background(), bson.D{})
68+
assert.Nil(mt, res.Err())
69+
}()
70+
}
71+
wg.Wait()
72+
73+
// Gather GetSucceeded, GetFailed and PoolCleared pool events.
74+
events := tpm.Events(func(e *event.PoolEvent) bool {
75+
getSucceeded := e.Type == event.GetSucceeded
76+
getFailed := e.Type == event.GetFailed
77+
poolCleared := e.Type == event.PoolCleared
78+
return getSucceeded || getFailed || poolCleared
79+
})
80+
81+
// Assert that first check out succeeds, pool is cleared, and second check
82+
// out fails due to connection error.
83+
assert.True(mt, len(events) >= 3, "expected at least 3 events, got %v", len(events))
84+
assert.Equal(mt, event.GetSucceeded, events[0].Type,
85+
"expected ConnectionCheckedOut event, got %v", events[0].Type)
86+
assert.Equal(mt, event.PoolCleared, events[1].Type,
87+
"expected ConnectionPoolCleared event, got %v", events[1].Type)
88+
assert.Equal(mt, event.GetFailed, events[2].Type,
89+
"expected ConnectionCheckedOutFailed event, got %v", events[2].Type)
90+
assert.Equal(mt, event.ReasonConnectionErrored, events[2].Reason,
91+
"expected check out failure due to connection error, failed due to %q", events[2].Reason)
92+
93+
// Assert that three find CommandStartedEvents were observed.
94+
for i := 0; i < 3; i++ {
95+
cmdEvt := mt.GetStartedEvent()
96+
assert.NotNil(mt, cmdEvt, "expected a find event, got nil")
97+
assert.Equal(mt, cmdEvt.CommandName, "find",
98+
"expected a find event, got a(n) %v event", cmdEvt.CommandName)
99+
}
100+
})
101+
}

mongo/integration/retryable_writes_prose_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ package integration
99
import (
1010
"bytes"
1111
"context"
12+
"sync"
1213
"testing"
14+
"time"
1315

1416
"go.mongodb.org/mongo-driver/bson"
17+
"go.mongodb.org/mongo-driver/event"
1518
"go.mongodb.org/mongo-driver/internal/testutil/assert"
1619
"go.mongodb.org/mongo-driver/mongo"
1720
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
@@ -137,4 +140,77 @@ func TestRetryableWritesProse(t *testing.T) {
137140
assert.NotNil(mt, err, "expected no txnNumber, got %v", txnNumber)
138141
})
139142
})
143+
144+
tpm := newTestPoolMonitor()
145+
// Client options with MaxPoolSize of 1 and RetryWrites used per the test description.
146+
// Lower HeartbeatInterval used to speed the test up for any server that uses streaming
147+
// heartbeats. Only connect to first host in list for sharded clusters.
148+
hosts := mtest.ClusterConnString().Hosts
149+
pceOpts := options.Client().SetMaxPoolSize(1).SetRetryWrites(true).
150+
SetPoolMonitor(tpm.PoolMonitor).SetHeartbeatInterval(500 * time.Millisecond).
151+
SetHosts(hosts[:1])
152+
153+
mtPceOpts := mtest.NewOptions().ClientOptions(pceOpts).MinServerVersion("4.3").
154+
Topologies(mtest.ReplicaSet, mtest.Sharded)
155+
mt.RunOpts("PoolClearedError retryability", mtPceOpts, func(mt *mtest.T) {
156+
// Force Find to block for 1 second once.
157+
mt.SetFailPoint(mtest.FailPoint{
158+
ConfigureFailPoint: "failCommand",
159+
Mode: mtest.FailPointMode{
160+
Times: 1,
161+
},
162+
Data: mtest.FailPointData{
163+
FailCommands: []string{"insert"},
164+
ErrorCode: 91,
165+
BlockConnection: true,
166+
BlockTimeMS: 1000,
167+
ErrorLabels: &[]string{"RetryableWriteError"},
168+
},
169+
})
170+
171+
// Clear CMAP and command events.
172+
tpm.ClearEvents()
173+
mt.ClearEvents()
174+
175+
// Perform an InsertOne on two different threads and assert both operations are
176+
// successful.
177+
var wg sync.WaitGroup
178+
for i := 0; i < 2; i++ {
179+
wg.Add(1)
180+
go func() {
181+
defer wg.Done()
182+
_, err := mt.Coll.InsertOne(context.Background(), bson.D{{"x", 1}})
183+
assert.Nil(mt, err, "InsertOne error: %v", err)
184+
}()
185+
}
186+
wg.Wait()
187+
188+
// Gather GetSucceeded, GetFailed and PoolCleared pool events.
189+
events := tpm.Events(func(e *event.PoolEvent) bool {
190+
getSucceeded := e.Type == event.GetSucceeded
191+
getFailed := e.Type == event.GetFailed
192+
poolCleared := e.Type == event.PoolCleared
193+
return getSucceeded || getFailed || poolCleared
194+
})
195+
196+
// Assert that first check out succeeds, pool is cleared, and second check
197+
// out fails due to connection error.
198+
assert.True(mt, len(events) >= 3, "expected at least 3 events, got %v", len(events))
199+
assert.Equal(mt, event.GetSucceeded, events[0].Type,
200+
"expected ConnectionCheckedOut event, got %v", events[0].Type)
201+
assert.Equal(mt, event.PoolCleared, events[1].Type,
202+
"expected ConnectionPoolCleared event, got %v", events[1].Type)
203+
assert.Equal(mt, event.GetFailed, events[2].Type,
204+
"expected ConnectionCheckedOutFailed event, got %v", events[2].Type)
205+
assert.Equal(mt, event.ReasonConnectionErrored, events[2].Reason,
206+
"expected check out failure due to connection error, failed due to %q", events[2].Reason)
207+
208+
// Assert that three insert CommandStartedEvents were observed.
209+
for i := 0; i < 3; i++ {
210+
cmdEvt := mt.GetStartedEvent()
211+
assert.NotNil(mt, cmdEvt, "expected an insert event, got nil")
212+
assert.Equal(mt, cmdEvt.CommandName, "insert",
213+
"expected an insert event, got a(n) %v event", cmdEvt.CommandName)
214+
}
215+
})
140216
}

0 commit comments

Comments
 (0)