Skip to content

Commit d72f279

Browse files
committed
update retry behavior and tests
1 parent 1e6973b commit d72f279

17 files changed

+832
-230
lines changed

pymongo/asynchronous/mongo_client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,7 +2802,10 @@ async def run(self) -> T:
28022802
if isinstance(exc, (ConnectionFailure, OperationFailure)):
28032803
# ConnectionFailures do not supply a code property
28042804
exc_code = getattr(exc, "code", None)
2805-
always_retryable = exc.has_error_label("RetryableError") and self._retryable
2805+
always_retryable = (
2806+
exc.has_error_label("RetryableError")
2807+
and self._client._options.retry_reads
2808+
)
28062809
overloaded = exc.has_error_label("SystemOverloadedError")
28072810
if not always_retryable and (
28082811
self._is_not_eligible_for_retry()
@@ -2826,7 +2829,8 @@ async def run(self) -> T:
28262829
exc_to_check = exc.error
28272830
retryable_write_label = exc_to_check.has_error_label("RetryableWriteError")
28282831
always_retryable = (
2829-
exc_to_check.has_error_label("RetryableError") and self._retryable
2832+
exc_to_check.has_error_label("RetryableError")
2833+
and self._client._options.retry_writes
28302834
)
28312835
overloaded = exc_to_check.has_error_label("SystemOverloadedError")
28322836
if not self._retryable and not always_retryable:

pymongo/synchronous/mongo_client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,7 +2792,10 @@ def run(self) -> T:
27922792
if isinstance(exc, (ConnectionFailure, OperationFailure)):
27932793
# ConnectionFailures do not supply a code property
27942794
exc_code = getattr(exc, "code", None)
2795-
always_retryable = exc.has_error_label("RetryableError") and self._retryable
2795+
always_retryable = (
2796+
exc.has_error_label("RetryableError")
2797+
and self._client._options.retry_reads
2798+
)
27962799
overloaded = exc.has_error_label("SystemOverloadedError")
27972800
if not always_retryable and (
27982801
self._is_not_eligible_for_retry()
@@ -2816,7 +2819,8 @@ def run(self) -> T:
28162819
exc_to_check = exc.error
28172820
retryable_write_label = exc_to_check.has_error_label("RetryableWriteError")
28182821
always_retryable = (
2819-
exc_to_check.has_error_label("RetryableError") and self._retryable
2822+
exc_to_check.has_error_label("RetryableError")
2823+
and self._client._options.retry_writes
28202824
)
28212825
overloaded = exc_to_check.has_error_label("SystemOverloadedError")
28222826
if not self._retryable and not always_retryable:

test/asynchronous/test_connection_monitoring.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
async_wait_until,
3333
camel_to_snake,
3434
)
35+
from test.version import Version
3536

3637
from bson.objectid import ObjectId
3738
from bson.son import SON
@@ -145,6 +146,10 @@ async def ready(self, op):
145146
"""Run the 'ready' operation."""
146147
await self.pool.ready()
147148

149+
async def backoff(self, op):
150+
"""Run the 'backoff' operation."""
151+
await self.pool.backoff()
152+
148153
async def clear(self, op):
149154
"""Run the 'clear' operation."""
150155
if "interruptInUseConnections" in op:
@@ -224,6 +229,20 @@ async def run_scenario(self, scenario_def, test):
224229
self.listener = CMAPListener()
225230
self._ops: list = []
226231

232+
if "runOn" in test:
233+
for run_reqs in test["runOn"]:
234+
if "minServerVersion" in run_reqs:
235+
other_version = Version.from_string(run_reqs["minServerVersion"])
236+
if async_client_context.version < other_version:
237+
self.skipTest(f"Server version must be at least {other_version}")
238+
if "maxServerVersion" in run_reqs:
239+
other_version = Version.from_string(run_reqs["maxServerVersion"])
240+
if async_client_context.version > other_version:
241+
self.skipTest(f"Server version must be at most {other_version}")
242+
if "poolBackoff" in run_reqs:
243+
if run_reqs["poolBackoff"] is False:
244+
self.skipTest("We support poolBackoff")
245+
227246
# Configure the fail point before creating the client.
228247
if "failPoint" in test:
229248
fp = test["failPoint"]

test/asynchronous/unified_format.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ async def is_run_on_requirement_satisfied(requirement):
158158
csfle_satisfied = _HAVE_PYMONGOCRYPT and min_version_satisfied
159159

160160
pool_backoff_statisfied = True
161-
req_pool_backoff = requirement.get("supportsPoolBackoff")
161+
req_pool_backoff = requirement.get("poolBackoff")
162162
if req_pool_backoff is False:
163163
pool_backoff_statisfied = False
164164

test/connection_logging/connection-logging.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,8 @@
523523
"description": "Connection enters backoff on closed connection",
524524
"runOnRequirements": [
525525
{
526-
"minServerVersion": "4.4"
526+
"minServerVersion": "4.4",
527+
"poolBackoff": true
527528
}
528529
],
529530
"operations": [
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"version": 1,
3+
"style": "integration",
4+
"description": "backoff closes pending connections",
5+
"runOn": [
6+
{
7+
"minServerVersion": "4.9.0",
8+
"poolBackoff": true
9+
}
10+
],
11+
"failPoint": {
12+
"configureFailPoint": "failCommand",
13+
"mode": "alwaysOn",
14+
"data": {
15+
"failCommands": [
16+
"isMaster",
17+
"hello"
18+
],
19+
"closeConnection": false,
20+
"blockConnection": true,
21+
"blockTimeMS": 10000
22+
}
23+
},
24+
"poolOptions": {
25+
"minPoolSize": 0
26+
},
27+
"operations": [
28+
{
29+
"name": "ready"
30+
},
31+
{
32+
"name": "start",
33+
"target": "thread1"
34+
},
35+
{
36+
"name": "checkOut",
37+
"thread": "thread1"
38+
},
39+
{
40+
"name": "waitForEvent",
41+
"event": "ConnectionCreated",
42+
"count": 1
43+
},
44+
{
45+
"name": "backoff"
46+
},
47+
{
48+
"name": "waitForEvent",
49+
"event": "ConnectionCheckOutFailed",
50+
"count": 1
51+
}
52+
],
53+
"events": [
54+
{
55+
"type": "ConnectionCheckOutStarted"
56+
},
57+
{
58+
"type": "ConnectionCreated"
59+
},
60+
{
61+
"type": "ConnectionPoolBackoff"
62+
},
63+
{
64+
"type": "ConnectionClosed"
65+
},
66+
{
67+
"type": "ConnectionCheckOutFailed"
68+
}
69+
],
70+
"ignore": [
71+
"ConnectionCheckedIn",
72+
"ConnectionCheckedOut",
73+
"ConnectionPoolCreated",
74+
"ConnectionPoolReady"
75+
]
76+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"version": 1,
3+
"style": "integration",
4+
"description": "error during minPoolSize population clears pool",
5+
"runOn": [
6+
{
7+
"minServerVersion": "4.9.0",
8+
"poolBackoff": true
9+
}
10+
],
11+
"failPoint": {
12+
"configureFailPoint": "failCommand",
13+
"mode": "alwaysOn",
14+
"data": {
15+
"failCommands": [
16+
"isMaster",
17+
"hello"
18+
],
19+
"errorCode": 18,
20+
"appName": "poolCreateMinSizeErrorTest"
21+
}
22+
},
23+
"poolOptions": {
24+
"minPoolSize": 1,
25+
"backgroundThreadIntervalMS": 50,
26+
"appName": "poolCreateMinSizeErrorTest"
27+
},
28+
"operations": [
29+
{
30+
"name": "ready"
31+
},
32+
{
33+
"name": "waitForEvent",
34+
"event": "ConnectionPoolCleared",
35+
"count": 1
36+
},
37+
{
38+
"name": "wait",
39+
"ms": 200
40+
}
41+
],
42+
"events": [
43+
{
44+
"type": "ConnectionPoolReady",
45+
"address": 42
46+
},
47+
{
48+
"type": "ConnectionCreated",
49+
"address": 42
50+
},
51+
{
52+
"type": "ConnectionClosed",
53+
"address": 42,
54+
"connectionId": 42,
55+
"reason": "error"
56+
},
57+
{
58+
"type": "ConnectionPoolCleared",
59+
"address": 42
60+
}
61+
],
62+
"ignore": [
63+
"ConnectionPoolCreated"
64+
]
65+
}

test/connection_monitoring/pool-create-min-size-error.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@
44
"description": "error during minPoolSize population clears pool",
55
"runOn": [
66
{
7-
"minServerVersion": "4.9.0"
7+
"minServerVersion": "4.9.0",
8+
"poolBackoff": false
89
}
910
],
1011
"failPoint": {
1112
"configureFailPoint": "failCommand",
12-
"mode": "alwaysOn",
13+
"mode": {
14+
"times": 50
15+
},
1316
"data": {
1417
"failCommands": [
1518
"isMaster",
1619
"hello"
1720
],
18-
"errorCode": 18,
21+
"closeConnection": true,
1922
"appName": "poolCreateMinSizeErrorTest"
2023
}
2124
},
@@ -47,15 +50,15 @@
4750
"type": "ConnectionCreated",
4851
"address": 42
4952
},
53+
{
54+
"type": "ConnectionPoolCleared",
55+
"address": 42
56+
},
5057
{
5158
"type": "ConnectionClosed",
5259
"address": 42,
5360
"connectionId": 42,
5461
"reason": "error"
55-
},
56-
{
57-
"type": "ConnectionPoolCleared",
58-
"address": 42
5962
}
6063
],
6164
"ignore": [

test/discovery_and_monitoring/unified/backoff-heartbeat-failure.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{
66
"minServerVersion": "4.4",
77
"serverless": "forbid",
8-
"supportsPoolBackoff": true,
8+
"poolBackoff": true,
99
"topologies": [
1010
"single",
1111
"replicaset",
@@ -137,7 +137,9 @@
137137
"arguments": {
138138
"client": "client",
139139
"event": {
140-
"poolBackoffEvent": {}
140+
"poolBackoffEvent": {
141+
"attempt": 1
142+
}
141143
},
142144
"count": 1
143145
}

0 commit comments

Comments
 (0)