Skip to content

Commit fd3154d

Browse files
committed
PYTHON-525 Don't reset whole Cluster on network error.
According to the spec, clear the pool for the server we were disconnected from and mark the server Unknown, but don't update other servers. This is a change from PyMongo 2's MongoReplicaSetClient, which reset all servers and their pools when disconnected from the primary specifically. The spec's justification is, "since application sockets are used frequently, a network error likely means the server has just become unavailable, so an immediate refresh is likely to get a network error, too. The server will not remain Unknown forever. It will be refreshed by the next periodic check or, if an application operation needs the server sooner than that, then a re-check will be triggered by the server selection algorithm." If the primary has gone down and we are in the midst of an election, marking all servers Unknown and forcing a re-scan will simply detect that all servers are currently secondaries, which the client knows anyway.
1 parent 5d5c983 commit fd3154d

2 files changed

Lines changed: 46 additions & 8 deletions

File tree

pymongo/mongo_client.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -810,22 +810,17 @@ def _send_message_with_response(
810810
exhaust)
811811

812812
def _reset_on_error(self, server, fn, *args, **kwargs):
813-
"""Execute an operation. Reset the pool on network error.
813+
"""Execute an operation. Reset the server on network error.
814814
815815
Returns fn()'s return value on success. On error, clears the server's
816-
pool and marks the server Unknown or, if the server is the primary,
817-
resets all pools and servers.
816+
pool and marks the server Unknown.
818817
819818
Re-raises any exception thrown by fn().
820819
"""
821820
try:
822821
return fn(*args, **kwargs)
823822
except ConnectionFailure:
824-
if server.description.is_writable:
825-
self.disconnect()
826-
else:
827-
self._cluster.reset_server(server.address)
828-
823+
self._cluster.reset_server(server.description.address)
829824
raise
830825

831826
def start_request(self):

test/test_client.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
OperationFailure,
4040
CursorNotFound)
4141
from pymongo.server_selectors import writable_server_selector
42+
from pymongo.server_type import SERVER_TYPE
4243
from test import (client_context,
4344
client_knobs,
4445
connection_string,
@@ -1109,6 +1110,48 @@ def test_reconnect(self):
11091110
self.assertEqual('a', c.host)
11101111
self.assertEqual(1, c.port)
11111112

1113+
def test_network_error_on_operation(self):
1114+
# Verify only the disconnected server is reset by a network failure.
1115+
1116+
# Disable background refresh.
1117+
with client_knobs(heartbeat_frequency=999999):
1118+
c = MockClient(
1119+
standalones=[],
1120+
members=['a:1', 'b:2'],
1121+
mongoses=[],
1122+
host='a:1',
1123+
replicaSet='rs',
1124+
connect=False)
1125+
1126+
# Set host-specific information so we can test whether it is reset.
1127+
c.set_wire_version_range('a:1', 0, 1)
1128+
c.set_wire_version_range('b:2', 0, 2)
1129+
1130+
connected(c)
1131+
wait_until(lambda: len(c.nodes) == 2, 'connect')
1132+
1133+
sd = c._get_cluster().get_server_by_address(('a', 1)).description
1134+
self.assertEqual(SERVER_TYPE.RSPrimary, sd.server_type)
1135+
self.assertEqual(0, sd.min_wire_version)
1136+
self.assertEqual(1, sd.max_wire_version)
1137+
1138+
c.kill_host('a:1')
1139+
1140+
# MongoClient is disconnected from the primary.
1141+
self.assertRaises(AutoReconnect, c.db.collection.find_one)
1142+
1143+
# The primary's description is reset.
1144+
sd_a = c._get_cluster().get_server_by_address(('a', 1)).description
1145+
self.assertEqual(SERVER_TYPE.Unknown, sd_a.server_type)
1146+
self.assertEqual(0, sd_a.min_wire_version)
1147+
self.assertEqual(0, sd_a.max_wire_version)
1148+
1149+
# ...but not the secondary's.
1150+
sd_b = c._get_cluster().get_server_by_address(('b', 2)).description
1151+
self.assertEqual(SERVER_TYPE.RSSecondary, sd_b.server_type)
1152+
self.assertEqual(0, sd_b.min_wire_version)
1153+
self.assertEqual(2, sd_b.max_wire_version)
1154+
11121155

11131156
if __name__ == "__main__":
11141157
unittest.main()

0 commit comments

Comments
 (0)