Skip to content

Commit aa41e70

Browse files
authored
PYTHON-5369 - Re-raise socket.timeout errors if the deadline has alre… (#2326)
1 parent 2655bb4 commit aa41e70

File tree

8 files changed

+33
-20
lines changed

8 files changed

+33
-20
lines changed

pymongo/asynchronous/pool.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class AsyncConnection:
131131
:param pool: a Pool instance
132132
:param address: the server's (host, port)
133133
:param id: the id of this socket in it's pool
134+
:param is_sdam: SDAM connections do not call hello on creation
134135
"""
135136

136137
def __init__(
@@ -139,11 +140,13 @@ def __init__(
139140
pool: Pool,
140141
address: tuple[str, int],
141142
id: int,
143+
is_sdam: bool,
142144
):
143145
self.pool_ref = weakref.ref(pool)
144146
self.conn = conn
145147
self.address = address
146148
self.id = id
149+
self.is_sdam = is_sdam
147150
self.closed = False
148151
self.last_checkin_time = time.monotonic()
149152
self.performed_handshake = False
@@ -711,13 +714,13 @@ def __init__(
711714
self,
712715
address: _Address,
713716
options: PoolOptions,
714-
handshake: bool = True,
717+
is_sdam: bool = False,
715718
client_id: Optional[ObjectId] = None,
716719
):
717720
"""
718721
:param address: a (hostname, port) tuple
719722
:param options: a PoolOptions instance
720-
:param handshake: whether to call hello for each new AsyncConnection
723+
:param is_sdam: whether to call hello for each new AsyncConnection
721724
"""
722725
if options.pause_enabled:
723726
self.state = PoolState.PAUSED
@@ -746,14 +749,14 @@ def __init__(
746749
self.pid = os.getpid()
747750
self.address = address
748751
self.opts = options
749-
self.handshake = handshake
752+
self.is_sdam = is_sdam
750753
# Don't publish events or logs in Monitor pools.
751754
self.enabled_for_cmap = (
752-
self.handshake
755+
not self.is_sdam
753756
and self.opts._event_listeners is not None
754757
and self.opts._event_listeners.enabled_for_cmap
755758
)
756-
self.enabled_for_logging = self.handshake
759+
self.enabled_for_logging = not self.is_sdam
757760

758761
# The first portion of the wait queue.
759762
# Enforces: maxPoolSize
@@ -1058,14 +1061,14 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
10581061

10591062
raise
10601063

1061-
conn = AsyncConnection(networking_interface, self, self.address, conn_id) # type: ignore[arg-type]
1064+
conn = AsyncConnection(networking_interface, self, self.address, conn_id, self.is_sdam) # type: ignore[arg-type]
10621065
async with self.lock:
10631066
self.active_contexts.add(conn.cancel_context)
10641067
self.active_contexts.discard(tmp_context)
10651068
if tmp_context.cancelled:
10661069
conn.cancel_context.cancel()
10671070
try:
1068-
if self.handshake:
1071+
if not self.is_sdam:
10691072
await conn.hello()
10701073
self.is_writable = conn.is_writable
10711074
if handler:

pymongo/asynchronous/topology.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ def _create_pool_for_monitor(self, address: _Address) -> Pool:
985985
)
986986

987987
return self._settings.pool_class(
988-
address, monitor_pool_options, handshake=False, client_id=self._topology_id
988+
address, monitor_pool_options, is_sdam=True, client_id=self._topology_id
989989
)
990990

991991
def _error_message(self, selector: Callable[[Selection], Selection]) -> str:

pymongo/network_layer.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,12 @@ def receive_data(conn: Connection, length: int, deadline: Optional[float]) -> me
357357
except socket.timeout:
358358
if conn.cancel_context.cancelled:
359359
raise _OperationCancelled("operation cancelled") from None
360-
if _PYPY:
360+
if (
361+
_PYPY
362+
or not conn.is_sdam
363+
and deadline is not None
364+
and deadline - time.monotonic() < 0
365+
):
361366
# We reached the true deadline.
362367
raise
363368
continue

pymongo/synchronous/pool.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class Connection:
131131
:param pool: a Pool instance
132132
:param address: the server's (host, port)
133133
:param id: the id of this socket in it's pool
134+
:param is_sdam: SDAM connections do not call hello on creation
134135
"""
135136

136137
def __init__(
@@ -139,11 +140,13 @@ def __init__(
139140
pool: Pool,
140141
address: tuple[str, int],
141142
id: int,
143+
is_sdam: bool,
142144
):
143145
self.pool_ref = weakref.ref(pool)
144146
self.conn = conn
145147
self.address = address
146148
self.id = id
149+
self.is_sdam = is_sdam
147150
self.closed = False
148151
self.last_checkin_time = time.monotonic()
149152
self.performed_handshake = False
@@ -709,13 +712,13 @@ def __init__(
709712
self,
710713
address: _Address,
711714
options: PoolOptions,
712-
handshake: bool = True,
715+
is_sdam: bool = False,
713716
client_id: Optional[ObjectId] = None,
714717
):
715718
"""
716719
:param address: a (hostname, port) tuple
717720
:param options: a PoolOptions instance
718-
:param handshake: whether to call hello for each new Connection
721+
:param is_sdam: whether to call hello for each new Connection
719722
"""
720723
if options.pause_enabled:
721724
self.state = PoolState.PAUSED
@@ -744,14 +747,14 @@ def __init__(
744747
self.pid = os.getpid()
745748
self.address = address
746749
self.opts = options
747-
self.handshake = handshake
750+
self.is_sdam = is_sdam
748751
# Don't publish events or logs in Monitor pools.
749752
self.enabled_for_cmap = (
750-
self.handshake
753+
not self.is_sdam
751754
and self.opts._event_listeners is not None
752755
and self.opts._event_listeners.enabled_for_cmap
753756
)
754-
self.enabled_for_logging = self.handshake
757+
self.enabled_for_logging = not self.is_sdam
755758

756759
# The first portion of the wait queue.
757760
# Enforces: maxPoolSize
@@ -1054,14 +1057,14 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
10541057

10551058
raise
10561059

1057-
conn = Connection(networking_interface, self, self.address, conn_id) # type: ignore[arg-type]
1060+
conn = Connection(networking_interface, self, self.address, conn_id, self.is_sdam) # type: ignore[arg-type]
10581061
with self.lock:
10591062
self.active_contexts.add(conn.cancel_context)
10601063
self.active_contexts.discard(tmp_context)
10611064
if tmp_context.cancelled:
10621065
conn.cancel_context.cancel()
10631066
try:
1064-
if self.handshake:
1067+
if not self.is_sdam:
10651068
conn.hello()
10661069
self.is_writable = conn.is_writable
10671070
if handler:

pymongo/synchronous/topology.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ def _create_pool_for_monitor(self, address: _Address) -> Pool:
983983
)
984984

985985
return self._settings.pool_class(
986-
address, monitor_pool_options, handshake=False, client_id=self._topology_id
986+
address, monitor_pool_options, is_sdam=True, client_id=self._topology_id
987987
)
988988

989989
def _error_message(self, selector: Callable[[Selection], Selection]) -> str:

test/asynchronous/utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def __init__(self):
159159
self.cancel_context = _CancellationContext()
160160
self.more_to_come = False
161161
self.id = random.randint(0, 100)
162+
self.is_sdam = False
162163
self.server_connection_id = random.randint(0, 100)
163164

164165
def close_conn(self, reason):
@@ -172,7 +173,7 @@ def __aexit__(self, exc_type, exc_val, exc_tb):
172173

173174

174175
class AsyncMockPool:
175-
def __init__(self, address, options, handshake=True, client_id=None):
176+
def __init__(self, address, options, is_sdam=False, client_id=None):
176177
self.gen = _PoolGeneration()
177178
self._lock = _async_create_lock()
178179
self.opts = options

test/test_topology.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def test_timeout_configuration(self):
121121
self.assertEqual(1, monitor._pool.opts.socket_timeout)
122122

123123
# The monitor, not its pool, is responsible for calling hello.
124-
self.assertFalse(monitor._pool.handshake)
124+
self.assertTrue(monitor._pool.is_sdam)
125125

126126

127127
class TestSingleServerTopology(TopologyTest):

test/utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ def __init__(self):
157157
self.cancel_context = _CancellationContext()
158158
self.more_to_come = False
159159
self.id = random.randint(0, 100)
160+
self.is_sdam = False
160161
self.server_connection_id = random.randint(0, 100)
161162

162163
def close_conn(self, reason):
@@ -170,7 +171,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
170171

171172

172173
class MockPool:
173-
def __init__(self, address, options, handshake=True, client_id=None):
174+
def __init__(self, address, options, is_sdam=False, client_id=None):
174175
self.gen = _PoolGeneration()
175176
self._lock = _create_lock()
176177
self.opts = options

0 commit comments

Comments
 (0)