-
Notifications
You must be signed in to change notification settings - Fork 1.2k
PYTHON-5903 : Retry attempts should share a single stable operation_id #2901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
75f4e21
7237bf1
63312b0
643ccef
11223ce
b36e20b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,7 +95,7 @@ | |
| _log_client_error, | ||
| _log_or_warn, | ||
| ) | ||
| from pymongo.message import _CursorAddress, _GetMore, _Query | ||
| from pymongo.message import _CursorAddress, _GetMore, _Query, _randint | ||
| from pymongo.monitoring import ConnectionClosedReason, _EventListeners | ||
| from pymongo.operations import ( | ||
| DeleteMany, | ||
|
|
@@ -1793,10 +1793,13 @@ async def _checkout( | |
| async with _MongoClientErrorHandler(self, server, session) as err_handler: | ||
| # Reuse the pinned connection, if it exists. | ||
| if in_txn and session and session._pinned_connection: | ||
| err_handler.contribute_socket(session._pinned_connection) | ||
| yield session._pinned_connection | ||
| conn = session._pinned_connection | ||
| conn.op_id = None | ||
| err_handler.contribute_socket(conn) | ||
| yield conn | ||
| return | ||
| async with await server.checkout(handler=err_handler) as conn: | ||
| conn.op_id = None # Only retryable read/write logic sets an op_id. | ||
| # Pin this session to the selected server or connection. | ||
| if ( | ||
| in_txn | ||
|
|
@@ -1837,6 +1840,8 @@ async def _select_server( | |
| be pinned to a mongos server address. | ||
| - `address` (optional): Address when sending a message | ||
| to a specific server, used for getMore. | ||
| - `operation_id` (optional): Stable operation id shared across retries, | ||
| used for command monitoring. | ||
| """ | ||
| try: | ||
| topology = await self._get_topology() | ||
|
|
@@ -1933,6 +1938,8 @@ async def _run_operation( | |
| async with operation.conn_mgr._lock: | ||
| async with _MongoClientErrorHandler(self, server, operation.session) as err_handler: # type: ignore[arg-type] | ||
| err_handler.contribute_socket(operation.conn_mgr.conn) | ||
| # Non-retryable getMore on a pinned conn; no shared op_id. | ||
| operation.conn_mgr.conn.op_id = None | ||
| return await run_with_conn( | ||
| operation.conn_mgr.conn, operation, operation.read_preference | ||
| ) | ||
|
|
@@ -2012,6 +2019,7 @@ async def _retry_internal( | |
| :param retryable: If the operation should be retried once, defaults to None | ||
| :param is_run_command: If this is a runCommand operation, defaults to False | ||
| :param is_aggregate_write: If this is a aggregate operation with a write, defaults to False. | ||
| :param operation_id: Stable operation id shared across retries, defaults to None | ||
|
|
||
| :return: Output of the calling func() | ||
| """ | ||
|
|
@@ -2058,6 +2066,7 @@ async def _retryable_read( | |
| (may not always be supported even if supplied), defaults to False | ||
| :param is_run_command: If this is a runCommand operation, defaults to False. | ||
| :param is_aggregate_write: If this is a aggregate operation with a write, defaults to False. | ||
| :param operation_id: Stable operation id shared across retries, defaults to None | ||
| """ | ||
|
|
||
| # Ensure that the client supports retrying on reads and there is no session in | ||
|
|
@@ -2101,6 +2110,7 @@ async def _retryable_write( | |
| :param session: Client session we will use to execute write operation | ||
| :param operation: The name of the operation that the server is being selected for | ||
| :param bulk: bulk abstraction to execute operations in bulk, defaults to None | ||
| :param operation_id: Stable operation id shared across retries, defaults to None | ||
| """ | ||
| async with self._tmp_session(session) as s: | ||
| return await self._retry_with_session(retryable, func, s, bulk, operation, operation_id) | ||
|
|
@@ -2217,6 +2227,8 @@ async def _kill_cursor_impl( | |
| namespace = address.namespace | ||
| db, coll = namespace.split(".", 1) | ||
| spec = {"killCursors": coll, "cursors": cursor_ids} | ||
| # killCursors is its own op; drop any op_id left on a pinned cursor conn. | ||
| conn.op_id = None | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A cleaner way of doing this might be to set
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. added |
||
| await conn.command(db, spec, session=session, client=self) | ||
|
|
||
| async def _process_kill_cursors(self) -> None: | ||
|
|
@@ -2784,7 +2796,7 @@ def __init__( | |
| self._server: Server = None # type: ignore | ||
| self._deprioritized_servers: list[Server] = [] | ||
| self._operation = operation | ||
| self._operation_id = operation_id | ||
| self._operation_id = operation_id if operation_id is not None else _randint() | ||
| self._attempt_number = 0 | ||
| self._is_run_command = is_run_command | ||
| self._is_aggregate_write = is_aggregate_write | ||
|
|
@@ -2990,6 +3002,7 @@ async def _write(self) -> T: | |
| is_mongos = False | ||
| self._server = await self._get_server() | ||
| async with self._client._checkout(self._server, self._session) as conn: | ||
| conn.op_id = self._operation_id | ||
| max_wire_version = conn.max_wire_version | ||
| sessions_supported = ( | ||
| self._session | ||
|
|
@@ -3029,6 +3042,7 @@ async def _read(self) -> T: | |
| conn, | ||
| read_pref, | ||
| ): | ||
| conn.op_id = self._operation_id | ||
| if self._retrying and not self._retryable and not self._always_retryable: | ||
| self._check_last_error() | ||
| if self._retrying: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should be moved to before the
if in_txnblock to prevent a pinned connection from re-using an old op_id.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added reset on this branch