Skip to content

PYTHON-5676 Consolidate command execution logic#2852

Open
aclark4life wants to merge 7 commits into
mongodb:masterfrom
aclark4life:PYTHON-5676
Open

PYTHON-5676 Consolidate command execution logic#2852
aclark4life wants to merge 7 commits into
mongodb:masterfrom
aclark4life:PYTHON-5676

Conversation

@aclark4life
Copy link
Copy Markdown
Contributor

@aclark4life aclark4life commented Jun 4, 2026

PYTHON-5676

Changes in this PR

Consolidates command execution into a single code path, the ticket's definition of done
("all database operations are done from a central location").

Previously the same execution skeleton — start clock → log/publish STARTED → send → receive → _process_response → _check_command_response → log/publish SUCCEEDED|FAILED — was copy-pasted across
five sites that differed only in how bytes hit the wire. This PR introduces a new module
pymongo/asynchronous/command_runner.py (auto-generating the sync mirror) whose run_command()
owns that entire skeleton: command logging and APM publishing together, the send/receive round
trip, $clusterTime gossip, _process_response, _check_command_response, failure conversion, and
auto-encryption decryption. Callers pass only what varies (the encoded message plus a few
transport/output hooks).

All five sites now route through run_command(), one per commit:

  • network.command() — raw async_sendall / async_receive_message transport.
  • Server.run_operation()conn transport with exhaust more_to_come, unpack_res, a
    reply_doc_builder for the find/getMore/explain APM reply format; keeps the Response /
    PinnedResponse wrapping at the call site.
  • bulk.write_command / unack_writeconn.write_command / conn.unack_write; pass
    process_response=False (process at the call site to preserve check → APM-succeed → process
    ordering) and decrypt_reply=False (bulk commands are encrypted up front).
  • client_bulk.write_command / unack_write — same, with the client-level {"error": exc}
    swallow kept at the call site.

New private API: pymongo.asynchronous.command_runner.run_command (+ sync mirror). No public API
changes.

A final commit renames the standard-command module network.pycommand_encoder.py. After
this consolidation it no longer does any networking — the send/receive round trip moved into
run_command — so it now only encodes a command and runs its pre-flight (read pref/concern,
collation, $clusterTime, encryption, CSOT, OP_MSG). The old name was misleading and collided with
the lower-level network_layer.py (raw sockets). Pure rename: git mv the async module (synchro
regenerates the sync mirror) plus the two pool.py import updates; no behavior change.

Differences from #2789

  • The runner lives under pymongo/asynchronous/ so it is async and is synchro-converted — the
    reverted _telemetry.py was top-level (sync-only) and structurally could not own the I/O.
  • It consolidates logging and APM together, not logging alone (the reverted PR left APM publishing
    duplicated at every call site).
  • The legacy OP_QUERY response shaping is preserved (is_command_response=use_cmd), not deleted —
    that dead-code cleanup is intentionally out of scope.
  • Subtle behaviors preserved exactly: bulk's check → APM-succeed → process ordering, the unack
    log-omits-documents / publish-includes-documents asymmetry, and client_bulk's swallow +
    {}-vs-exc.details gossip.

Test Plan

No new tests: this is a behavior-preserving refactor, and the existing APM/command-monitoring and
command-logging spec suites assert the exact event/log documents that any regression here would change.

Checklist

Checklist for Author

  • Did you update the changelog (if necessary)? — Not necessary; internal refactor with no
    user-facing behavior change.
  • Is there test coverage? — Covered by existing APM/logging/CRUD/bulk spec suites (behavior preserved).
  • Is any followup work tracked in a JIRA ticket? If so, add link(s). — Possible followup: remove the
    now-unreachable legacy OP_QUERY shaping (kept out of this PR by design); no ticket yet.

Checklist for Reviewer

  • Does the title of the PR reference a JIRA Ticket?
  • Do you fully understand the implementation? (Would you be comfortable explaining how this code works to someone else?)
  • Is all relevant documentation (README or docstring) updated?

…hrough it

Introduce pymongo/asynchronous/command_runner.py (auto-generates the sync
mirror), the single async code path for command execution. run_command owns
the full skeleton: STARTED/SUCCEEDED/FAILED command logging AND APM publishing
together, the network round trip, $clusterTime gossip, _process_response,
_check_command_response, failure conversion, and auto-encryption decryption.

Route network.command() through it, removing the duplicated logging/APM/send/
receive/decrypt block. Behavior is preserved byte-for-byte (logging and APM
event documents unchanged); no per-command object is allocated, so the hot
path is unchanged.
Extend run_command with the cursor transport (conn.send_message/receive_message,
exhaust more_to_come receive-only) and output hooks (unpack_res, cursor_id,
is_command_response for legacy OP_QUERY, pool_opts, command_name, ensure_db for
$db gossip, and a reply_doc_builder for the find/getMore/explain APM reply
format). run_operation now builds the message, supplies the reply-doc builder,
and keeps the Response/PinnedResponse wrapping; everything between is the shared
run_command path.

The legacy OP_QUERY response shaping is preserved (is_command_response=use_cmd),
not deleted -- that dead-code cleanup stays out of this consolidation. Behavior
(logging, APM events, exhaust/pinning, decryption) is unchanged.
Add process_response and decrypt_reply flags plus the conn.unack_write transport
to run_command, then route bulk.write_command (acknowledged) and
bulk.unack_write through it. The bulk paths pass process_response=False (they
run _process_response at the call site, preserving check -> APM-succeed ->
process ordering) and decrypt_reply=False (their commands are encrypted up
front). The unack path publishes a copy of the command carrying the docs field
while logging the bare command, matching the prior asymmetry.

Drops the duplicated logging/APM/failure-conversion blocks (and the unreachable
_convert_write_result-on-failure branch for unacknowledged writes).
Route client_bulk.write_command and client_bulk.unack_write through run_command
(process_response=False, decrypt_reply=False, conn.unack_write transport for the
unack path). The client-level swallow semantics stay at the call site: the
except wraps the raised error into reply={"error": exc} and runs the
$clusterTime gossip (exc.details for OperationFailure, else {}); the unack path
publishes a copy carrying ops/nsInfo while logging the bare command.

With this, all command execution -- standard commands, cursor find/getMore, and
both bulk write families -- flows through the single run_command path.
After the consolidation, this module no longer does any networking -- the
send/receive round trip moved into command_runner.run_command. It now only
encodes a command and runs its pre-flight (read preference/concern, collation,
$clusterTime, auto-encryption, CSOT, OP_MSG encoding), so 'network' was
misleading and collided with the lower-level network_layer.py (raw sockets).

Pure rename: git mv the async module (synchro regenerates the sync mirror) and
update the two pool.py imports. No behavior change.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 4, 2026

Codecov Report

❌ Patch coverage is 95.18072% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pymongo/asynchronous/server.py 69.23% 4 Missing ⚠️
pymongo/synchronous/server.py 69.23% 4 Missing ⚠️
pymongo/asynchronous/command_runner.py 97.72% 0 Missing and 2 partials ⚠️
pymongo/synchronous/command_runner.py 97.72% 0 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@aclark4life aclark4life marked this pull request as ready for review June 5, 2026 13:56
@aclark4life aclark4life requested a review from a team as a code owner June 5, 2026 13:56
@aclark4life aclark4life requested review from NoahStapp and Copilot June 5, 2026 13:56
@aclark4life
Copy link
Copy Markdown
Contributor Author

aclark4life commented Jun 5, 2026

Failures related to #2853

@aclark4life aclark4life requested a review from blink1073 June 5, 2026 14:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates MongoDB command execution (logging + command monitoring/APM publishing + send/receive + response processing/checking + clusterTime gossip + optional decryption) into a single shared implementation (run_command) and updates the major call sites (standard command path, cursor operations, bulk writes) to route through it. This reduces duplicated “command execution skeleton” logic while keeping behavioral compatibility.

Changes:

  • Introduces pymongo.{a,}synchronous.command_runner.run_command() as the central command execution path.
  • Refactors Server.run_operation() (cursor operations) and bulk/client-bulk write paths to use run_command() with hooks for legacy reply shaping and ordering constraints.
  • Renames/repurposes the former network command module into command_encoder to reflect its role as an encoder + pre-flight layer (with the actual I/O handled by run_command / network_layer).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
pymongo/asynchronous/command_runner.py Adds the async shared run_command() implementation (logging/APM + transport + response handling).
pymongo/synchronous/command_runner.py Adds the sync shared run_command() implementation (mirror of async via synchro).
pymongo/asynchronous/command_encoder.py Routes standard async command execution through run_command(); updates module docstring.
pymongo/synchronous/command_encoder.py Routes standard sync command execution through run_command(); updates module docstring.
pymongo/asynchronous/server.py Refactors async cursor operations to use run_command() with reply shaping for monitoring.
pymongo/synchronous/server.py Refactors sync cursor operations to use run_command() with reply shaping for monitoring.
pymongo/asynchronous/bulk.py Refactors async bulk write command/unack paths to use run_command() and preserve ordering.
pymongo/synchronous/bulk.py Refactors sync bulk write command/unack paths to use run_command() and preserve ordering.
pymongo/asynchronous/client_bulk.py Refactors async client-bulk write command/unack paths to use run_command() while preserving top-level error embedding behavior.
pymongo/synchronous/client_bulk.py Refactors sync client-bulk write command/unack paths to use run_command() while preserving top-level error embedding behavior.
pymongo/asynchronous/pool.py Updates import to use command_encoder.command after module rename.
pymongo/synchronous/pool.py Updates import to use command_encoder.command after module rename.
Comments suppressed due to low confidence (1)

pymongo/synchronous/command_encoder.py:21

  • Docstring references pymongo.command_runner.run_command, but there is no top-level pymongo.command_runner module in this codebase. Since this file imports run_command from pymongo.synchronous.command_runner, the Sphinx reference should match to avoid broken cross-references / confusion.

if bwc.publish:
bwc._start(cmd, request_id, op_docs, ns_docs)
try:
reply = bwc.conn.write_command(request_id, msg, bwc.codec) # type: ignore[misc, arg-type]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AsyncConnection.write_command doesn't appear to have any remaining callers, so it (and its sync counterpart) are dead code that should be removed.

result = await bwc.conn.unack_write(msg, max_doc_size) # type: ignore[func-returns-value, misc, override]
duration = datetime.datetime.now() - bwc.start_time
if result is not None:
reply = _convert_write_result(bwc.name, cmd, result) # type: ignore[arg-type]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_convert_write_result is also dead code now.

serviceId=bwc.conn.service_id,
)
if bwc.publish:
bwc._start(cmd, request_id, docs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_BulkWriteContext._start, _BulkWriteContext._succeed, _BulkWriteContext._fail, and _ClientBulkWriteContext._start are all dead code.

Comment thread pymongo/asynchronous/command_runner.py Outdated
_IS_SYNC = False


async def run_command(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This number of kwargs, several of which directly gate which network behavior to use, is extremely hard to parse and makes every call site much more complex than before. What if we split those different paths into three public methods that all wrap a private _run_command: one for acknowledged commands, one for unacknowledged commands, and one for cursor commands? That way we still have one unified command execution point, but multiple entry points that don't require a huge list of kwargs on every invocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants