Skip to content

Commit 9cca32b

Browse files
committed
PYTHON-5846 Address further PR review comments
- Restore original _raise_if_not_ready and _get_conn parameter names/order - Add docstrings to all public methods on _CmapTelemetry and _SdamTelemetry - Remove redundant _publish/_listener/_events attrs from Server.__init__ - Avoid creating two _ServerSelectionTelemetry per select_server call by threading ss through select_servers and _select_server - Make _ServerSelectionTelemetry.failed topology_description a required arg - Move SRV monitor failure log to telemetry.py (log_srv_monitor_failure)
1 parent 012c143 commit 9cca32b

7 files changed

Lines changed: 77 additions & 76 deletions

File tree

pymongo/_telemetry.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ def _emit_log(self, message: _ConnectionStatusMessage, **extra: Any) -> None:
245245
)
246246

247247
def pool_created(self, non_default_options: dict[str, Any]) -> None:
248+
"""Emit the pool created log entry and APM event."""
248249
# Log before publishing to prevent potential listener preemption in tests.
249250
if self._should_log:
250251
self._emit_log(_ConnectionStatusMessage.POOL_CREATED, **non_default_options)
@@ -253,6 +254,7 @@ def pool_created(self, non_default_options: dict[str, Any]) -> None:
253254
self._listeners.publish_pool_created(self._address, non_default_options)
254255

255256
def pool_ready(self) -> None:
257+
"""Emit the pool ready log entry and APM event."""
256258
# Log before publishing to prevent potential listener preemption in tests.
257259
if self._should_log:
258260
self._emit_log(_ConnectionStatusMessage.POOL_READY)
@@ -261,6 +263,7 @@ def pool_ready(self) -> None:
261263
self._listeners.publish_pool_ready(self._address)
262264

263265
def pool_cleared(self, service_id: Optional[ObjectId], interrupt_connections: bool) -> None:
266+
"""Emit the pool cleared log entry and APM event."""
264267
# Log before publishing to prevent potential listener preemption in tests.
265268
if self._should_log:
266269
self._emit_log(_ConnectionStatusMessage.POOL_CLEARED, serviceId=service_id)
@@ -273,6 +276,7 @@ def pool_cleared(self, service_id: Optional[ObjectId], interrupt_connections: bo
273276
)
274277

275278
def pool_closed(self) -> None:
279+
"""Emit the pool closed log entry and APM event."""
276280
# Log before publishing to prevent potential listener preemption in tests.
277281
if self._should_log:
278282
self._emit_log(_ConnectionStatusMessage.POOL_CLOSED)
@@ -281,6 +285,7 @@ def pool_closed(self) -> None:
281285
self._listeners.publish_pool_closed(self._address)
282286

283287
def connection_created(self, conn_id: int) -> None:
288+
"""Emit the connection created log entry and APM event."""
284289
# Log before publishing to prevent potential listener preemption in tests.
285290
if self._should_log:
286291
self._emit_log(_ConnectionStatusMessage.CONN_CREATED, driverConnectionId=conn_id)
@@ -289,6 +294,7 @@ def connection_created(self, conn_id: int) -> None:
289294
self._listeners.publish_connection_created(self._address, conn_id)
290295

291296
def connection_ready(self, conn_id: int, creation_time: float) -> None:
297+
"""Emit the connection ready log entry and APM event."""
292298
should_log = self._should_log
293299
should_publish = self._should_publish
294300
if not should_log and not should_publish:
@@ -306,6 +312,7 @@ def connection_ready(self, conn_id: int, creation_time: float) -> None:
306312
self._listeners.publish_connection_ready(self._address, conn_id, duration)
307313

308314
def connection_closed(self, conn_id: int, reason: str) -> None:
315+
"""Emit the connection closed log entry and APM event."""
309316
should_log = self._should_log
310317
should_publish = self._should_publish
311318
if should_publish:
@@ -330,6 +337,7 @@ def checkout_started(self) -> float:
330337
return start
331338

332339
def checkout_succeeded(self, conn_id: int, start: float) -> None:
340+
"""Emit the checkout succeeded log entry and APM event."""
333341
should_log = self._should_log
334342
should_publish = self._should_publish
335343
if not should_log and not should_publish:
@@ -346,6 +354,7 @@ def checkout_succeeded(self, conn_id: int, start: float) -> None:
346354
)
347355

348356
def checkout_failed(self, reason: str, error: str, start: float) -> None:
357+
"""Emit the checkout failed log entry and APM event."""
349358
should_log = self._should_log
350359
should_publish = self._should_publish
351360
if not should_log and not should_publish:
@@ -363,6 +372,7 @@ def checkout_failed(self, reason: str, error: str, start: float) -> None:
363372
)
364373

365374
def checked_in(self, conn_id: int) -> None:
375+
"""Emit the connection checked-in log entry and APM event."""
366376
if self._should_publish:
367377
assert self._listeners is not None
368378
self._listeners.publish_connection_checked_in(self._address, conn_id)
@@ -522,13 +532,15 @@ def _emit_log(self, message: _SDAMStatusMessage, **extra: Any) -> None:
522532
)
523533

524534
def topology_opened(self) -> None:
535+
"""Emit the topology opened log entry and APM event."""
525536
if self._should_log:
526537
self._emit_log(_SDAMStatusMessage.START_TOPOLOGY)
527538
if self._publish_tp:
528539
assert self._listeners is not None
529540
self._enqueue(self._listeners.publish_topology_opened, (self._topology_id,))
530541

531542
def topology_description_changed(self, old_td: Any, new_td: Any) -> None:
543+
"""Emit the topology description changed APM event and log entry."""
532544
if self._publish_tp:
533545
assert self._listeners is not None
534546
self._enqueue(
@@ -560,6 +572,7 @@ def topology_closed(self, old_td: Any, new_td: Any) -> None:
560572
self._emit_log(_SDAMStatusMessage.STOP_TOPOLOGY)
561573

562574
def server_opened(self, address: _Address) -> None:
575+
"""Emit the server opened log entry and APM event."""
563576
if self._publish_server:
564577
assert self._listeners is not None
565578
self._enqueue(self._listeners.publish_server_opened, (address, self._topology_id))
@@ -571,6 +584,7 @@ def server_opened(self, address: _Address) -> None:
571584
)
572585

573586
def server_description_changed(self, sd_old: Any, sd_new: Any, address: _Address) -> None:
587+
"""Emit the server description changed APM event."""
574588
if self._publish_server:
575589
assert self._listeners is not None
576590
self._enqueue(
@@ -579,6 +593,7 @@ def server_description_changed(self, sd_old: Any, sd_new: Any, address: _Address
579593
)
580594

581595
def server_closed(self, address: _Address) -> None:
596+
"""Emit the server closed log entry and APM event."""
582597
if self._publish_server:
583598
assert self._listeners is not None
584599
self._enqueue(self._listeners.publish_server_closed, (address, self._topology_id))
@@ -653,14 +668,12 @@ def waiting(self, remaining_time_ms: int) -> None:
653668
remainingTimeMS=remaining_time_ms,
654669
)
655670

656-
def failed(self, failure: str, topology_description: Any = None) -> None:
671+
def failed(self, failure: str, topology_description: Any) -> None:
657672
"""Emit the server selection FAILED log entry with the current topology description."""
658673
if self._should_log:
659674
self._emit_log(
660675
_ServerSelectionStatusMessage.FAILED,
661-
topology_description
662-
if topology_description is not None
663-
else self._topology_description,
676+
topology_description,
664677
failure=failure,
665678
)
666679

pymongo/asynchronous/pool.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -977,11 +977,11 @@ async def checkout(
977977
978978
:param handler: A _MongoClientErrorHandler.
979979
"""
980-
start = self._telemetry.checkout_started()
980+
checkout_started_time = self._telemetry.checkout_started()
981981

982-
conn = await self._get_conn(handler=handler, checkout_start=start)
982+
conn = await self._get_conn(checkout_started_time, handler=handler)
983983

984-
self._telemetry.checkout_succeeded(conn.id, start)
984+
self._telemetry.checkout_succeeded(conn.id, checkout_started_time)
985985
try:
986986
async with self.lock:
987987
self.active_contexts.add(conn.cancel_context)
@@ -1012,13 +1012,13 @@ async def checkout(
10121012
elif conn.active:
10131013
await self.checkin(conn)
10141014

1015-
def _raise_if_not_ready(self, emit_event: bool, checkout_start: float) -> None:
1015+
def _raise_if_not_ready(self, checkout_started_time: float, emit_event: bool) -> None:
10161016
if self.state != PoolState.READY:
10171017
if emit_event:
10181018
self._telemetry.checkout_failed(
10191019
"An error occurred while trying to establish a new connection",
10201020
ConnectionCheckOutFailedReason.CONN_ERROR,
1021-
checkout_start,
1021+
checkout_started_time,
10221022
)
10231023

10241024
details = _get_timeout_details(self.opts)
@@ -1027,7 +1027,7 @@ def _raise_if_not_ready(self, emit_event: bool, checkout_start: float) -> None:
10271027
)
10281028

10291029
async def _get_conn(
1030-
self, handler: Optional[_MongoClientErrorHandler] = None, checkout_start: float = 0.0
1030+
self, checkout_started_time: float, handler: Optional[_MongoClientErrorHandler] = None
10311031
) -> AsyncConnection:
10321032
"""Get or create a AsyncConnection. Can raise ConnectionFailure."""
10331033
# We use the pid here to avoid issues with fork / multiprocessing.
@@ -1040,7 +1040,7 @@ async def _get_conn(
10401040
self._telemetry.checkout_failed(
10411041
"Connection pool was closed",
10421042
ConnectionCheckOutFailedReason.POOL_CLOSED,
1043-
checkout_start,
1043+
checkout_started_time,
10441044
)
10451045
raise _PoolClosedError(
10461046
"Attempted to check out a connection from closed connection pool"
@@ -1058,16 +1058,16 @@ async def _get_conn(
10581058
deadline = None
10591059

10601060
async with self.size_cond:
1061-
self._raise_if_not_ready(emit_event=True, checkout_start=checkout_start)
1061+
self._raise_if_not_ready(checkout_started_time, emit_event=True)
10621062
while not (self.requests < self.max_pool_size):
10631063
timeout = deadline - time.monotonic() if deadline else None
10641064
if not await _async_cond_wait(self.size_cond, timeout):
10651065
# Timed out, notify the next thread to ensure a
10661066
# timeout doesn't consume the condition.
10671067
if self.requests < self.max_pool_size:
10681068
self.size_cond.notify()
1069-
self._raise_wait_queue_timeout(checkout_start)
1070-
self._raise_if_not_ready(emit_event=True, checkout_start=checkout_start)
1069+
self._raise_wait_queue_timeout(checkout_started_time)
1070+
self._raise_if_not_ready(checkout_started_time, emit_event=True)
10711071
self.requests += 1
10721072

10731073
# We've now acquired the semaphore and must release it on error.
@@ -1082,7 +1082,7 @@ async def _get_conn(
10821082
# CMAP: we MUST wait for either maxConnecting OR for a socket
10831083
# to be checked back into the pool.
10841084
async with self._max_connecting_cond:
1085-
self._raise_if_not_ready(emit_event=False, checkout_start=checkout_start)
1085+
self._raise_if_not_ready(checkout_started_time, emit_event=False)
10861086
while not (self.conns or self._pending < self._max_connecting):
10871087
timeout = deadline - time.monotonic() if deadline else None
10881088
if not await _async_cond_wait(self._max_connecting_cond, timeout):
@@ -1091,8 +1091,8 @@ async def _get_conn(
10911091
if self.conns or self._pending < self._max_connecting:
10921092
self._max_connecting_cond.notify()
10931093
emitted_event = True
1094-
self._raise_wait_queue_timeout(checkout_start)
1095-
self._raise_if_not_ready(emit_event=False, checkout_start=checkout_start)
1094+
self._raise_wait_queue_timeout(checkout_started_time)
1095+
self._raise_if_not_ready(checkout_started_time, emit_event=False)
10961096

10971097
try:
10981098
conn = self.conns.popleft()
@@ -1124,7 +1124,7 @@ async def _get_conn(
11241124
self._telemetry.checkout_failed(
11251125
"An error occurred while trying to establish a new connection",
11261126
ConnectionCheckOutFailedReason.CONN_ERROR,
1127-
checkout_start,
1127+
checkout_started_time,
11281128
)
11291129
raise
11301130

@@ -1216,11 +1216,11 @@ async def _perished(self, conn: AsyncConnection) -> bool:
12161216

12171217
return False
12181218

1219-
def _raise_wait_queue_timeout(self, checkout_start: float) -> NoReturn:
1219+
def _raise_wait_queue_timeout(self, checkout_started_time: float) -> NoReturn:
12201220
self._telemetry.checkout_failed(
12211221
"Wait queue timeout elapsed without a connection becoming available",
12221222
ConnectionCheckOutFailedReason.TIMEOUT,
1223-
checkout_start,
1223+
checkout_started_time,
12241224
)
12251225
timeout = _csot.get_timeout() or self.opts.wait_queue_timeout
12261226
if self.opts.load_balanced:

pymongo/asynchronous/server.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,8 @@ def __init__(
6464
self._pool = pool
6565
self._monitor = monitor
6666
self._topology_id = topology_id
67-
self._publish = listeners is not None and listeners.enabled_for_server
68-
self._listener = listeners
69-
self._events = None
70-
if self._publish:
71-
self._events = events() # type: ignore[misc]
72-
self._sdam = _SdamTelemetry(topology_id, listeners, self._events) # type: ignore[arg-type]
67+
_events = events() if listeners is not None and listeners.enabled_for_server else None # type: ignore[misc]
68+
self._sdam = _SdamTelemetry(topology_id, listeners, _events) # type: ignore[arg-type]
7369

7470
async def open(self) -> None:
7571
"""Start monitoring, or restart after a fork.

pymongo/asynchronous/topology.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ async def select_servers(
227227
address: Optional[_Address] = None,
228228
operation_id: Optional[int] = None,
229229
deprioritized_servers: Optional[list[Server]] = None,
230-
) -> list[Server]:
230+
) -> tuple[list[Server], _ServerSelectionTelemetry]:
231231
"""Return a list of Servers matching selector, or time out.
232232
233233
:param selector: function that takes a list of Servers and returns
@@ -253,7 +253,7 @@ async def select_servers(
253253
await self.cleanup_monitors()
254254

255255
async with self._lock:
256-
server_descriptions = await self._select_servers_loop(
256+
server_descriptions, ss = await self._select_servers_loop(
257257
selector,
258258
server_timeout,
259259
operation,
@@ -264,7 +264,7 @@ async def select_servers(
264264

265265
return [
266266
cast(Server, self.get_server_by_address(sd.address)) for sd in server_descriptions
267-
]
267+
], ss
268268

269269
async def _select_servers_loop(
270270
self,
@@ -274,7 +274,7 @@ async def _select_servers_loop(
274274
operation_id: Optional[int],
275275
address: Optional[_Address],
276276
deprioritized_servers: Optional[list[Server]] = None,
277-
) -> list[ServerDescription]:
277+
) -> tuple[list[ServerDescription], _ServerSelectionTelemetry]:
278278
"""select_servers() guts. Hold the lock when calling this."""
279279
now = time.monotonic()
280280
end_time = now + timeout
@@ -320,7 +320,7 @@ async def _select_servers_loop(
320320
)
321321

322322
self._description.check_compatible()
323-
return server_descriptions
323+
return server_descriptions, ss
324324

325325
async def _select_server(
326326
self,
@@ -330,8 +330,8 @@ async def _select_server(
330330
address: Optional[_Address] = None,
331331
deprioritized_servers: Optional[list[Server]] = None,
332332
operation_id: Optional[int] = None,
333-
) -> Server:
334-
servers = await self.select_servers(
333+
) -> tuple[Server, _ServerSelectionTelemetry]:
334+
servers, ss = await self.select_servers(
335335
selector,
336336
operation,
337337
server_selection_timeout,
@@ -340,12 +340,12 @@ async def _select_server(
340340
deprioritized_servers,
341341
)
342342
if len(servers) == 1:
343-
return servers[0]
343+
return servers[0], ss
344344
server1, server2 = random.sample(servers, 2)
345345
if server1.pool.operation_count <= server2.pool.operation_count:
346-
return server1
346+
return server1, ss
347347
else:
348-
return server2
348+
return server2, ss
349349

350350
async def select_server(
351351
self,
@@ -357,7 +357,7 @@ async def select_server(
357357
operation_id: Optional[int] = None,
358358
) -> Server:
359359
"""Like select_servers, but choose a random server if several match."""
360-
server = await self._select_server(
360+
server, ss = await self._select_server(
361361
selector,
362362
operation,
363363
server_selection_timeout,
@@ -367,9 +367,7 @@ async def select_server(
367367
)
368368
if _csot.get_timeout():
369369
_csot.set_rtt(server.description.min_round_trip_time)
370-
_ServerSelectionTelemetry(
371-
self._topology_id, selector, operation, operation_id, self.description
372-
).succeeded(server.description.address[0], server.description.address[1])
370+
ss.succeeded(server.description.address[0], server.description.address[1])
373371
return server
374372

375373
async def select_server_by_address(

0 commit comments

Comments
 (0)