Skip to content

PYTHON-5977 APM/logging hot-path optimizations - #2968

Open
NoahStapp wants to merge 10 commits into
mongodb:mainfrom
NoahStapp:BF-44986
Open

PYTHON-5977 APM/logging hot-path optimizations#2968
NoahStapp wants to merge 10 commits into
mongodb:mainfrom
NoahStapp:BF-44986

Conversation

@NoahStapp

@NoahStapp NoahStapp commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

PYTHON-5977

Changes in this PR

The changes introduced by PYTHON-5903, PYTHON-5846, PYTHON-5745, and PYTHON-5676 caused small but measurable regressions for low-latency, high operation count workloads such as the DSI locust_ping benchmark. Much of this is due to constructing telemetry objects even when APM/logging is disabled and other similar changes that lie on the hot path for such workloads.

This PR attempts to optimize all of those regressions away and return performance to its point before their addition.

Test Plan

Standard test suite, benchmarks.

Results of a simple ping workload benchmark:

Metric PR main main vs PR
Throughput 10,634/s 6,567/s −38.8%
Mean latency 752 µs 1,219 µs +62%
p50 ~170 µs ~800 µs ~4–5× worse
p99 ~5,000 µs ~7,000 µs +40%

Checklist

Checklist for Author

  • [ ] Did you update the changelog (if necessary)?
  • [ ] Is there test coverage?
  • [ ] Is any followup work tracked in a JIRA ticket? If so, add link(s).

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?

@codecov-commenter

codecov-commenter commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.40000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pymongo/_telemetry.py 90.47% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@NoahStapp
NoahStapp marked this pull request as ready for review August 4, 2026 18:05
@NoahStapp

Copy link
Copy Markdown
Contributor Author

Failures are unrelated.

@NoahStapp
NoahStapp requested a review from a team as a code owner August 4, 2026 18:05

Copilot AI left a comment

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.

Pull request overview

This PR targets performance regressions on low-latency, high-op-count workloads by removing avoidable APM/logging/telemetry work from hot paths when telemetry is disabled, while preserving existing behavior when telemetry is enabled.

Changes:

  • Gate operation-id generation and telemetry object construction behind “telemetry enabled” checks (APM listeners and/or DEBUG logging), especially on retry and bulk-write paths.
  • Reduce hot-path overhead in command execution, CMAP checkout/checkin, and server selection by skipping telemetry/logging work when disabled.
  • Add regression tests to ensure no RawBSON reply inflation and no operation-id generation when telemetry is disabled.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pymongo/_telemetry.py Adds helper gating and streamlines command/CMAP/SDAM telemetry to avoid work when disabled.
pymongo/monitoring.py Clarifies speculative-authentication redaction guard for potential RawBSONDocument inputs.
pymongo/message.py Allows passing optional operation_id through bulk-write context types.
pymongo/asynchronous/command_runner.py Skips command telemetry construction when APM/logging disabled; returns duration as seconds internally.
pymongo/synchronous/command_runner.py Sync equivalent of the async command-runner hot-path gating and duration handling.
pymongo/asynchronous/mongo_client.py Centralizes op-id generation gating for retries via _should_generate_op_id.
pymongo/synchronous/mongo_client.py Sync equivalent of op-id generation gating for retries.
pymongo/asynchronous/bulk.py Avoids op-id generation for bulk writes when telemetry disabled.
pymongo/synchronous/bulk.py Sync equivalent bulk-write op-id gating.
pymongo/asynchronous/client_bulk.py Avoids op-id generation for client bulk writes when telemetry disabled.
pymongo/synchronous/client_bulk.py Sync equivalent client-bulk op-id gating and type adjustments.
pymongo/asynchronous/pool.py Adds fast paths to skip CMAP telemetry/logging calls when disabled.
pymongo/synchronous/pool.py Sync equivalent pool fast paths for CMAP telemetry/logging.
pymongo/asynchronous/topology.py Avoids server-selection telemetry object creation and success logging when server-selection logging is disabled.
pymongo/synchronous/topology.py Sync equivalent server-selection logging gating.
test/asynchronous/test_operation_id_retry.py Adds coverage ensuring no op-id generation when telemetry disabled; covers bulk/client-bulk.
test/test_operation_id_retry.py Sync generated equivalent test coverage for no-op-id behavior when telemetry disabled.
test/asynchronous/test_monitoring.py Adds test ensuring CommandSucceededEvent redaction checks don’t inflate RawBSON replies for non-hello commands.
test/test_monitoring.py Sync generated equivalent of the RawBSON inflation regression test.
Suppressed comments (1)

pymongo/_telemetry.py:108

  • Grammar: “fast path … inline this gate” should use the verb form “inlines” (and the following sentence should refer to a singular subject).
        # NOTE: the _run_command fast path in command_runner.py inline this gate for performance
        # They must be kept in sync with any gating changes

Comment thread pymongo/_telemetry.py
# Fast path: skip telemetry construction when logging and APM are disabled
# Inline enabled check here for performance
telemetry: Optional[_CommandTelemetry] = None
if (topology_id is not None and _COMMAND_LOGGER.isEnabledFor(logging.DEBUG)) or (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this part should be a helper in _telemetry.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is intentionally inlined here for performance since this is called on every single operation, moving to a helper is a small performance hit that compounds across each time the inlined check is abstracted.

self.active_contexts.discard(conn.cancel_context)
self._telemetry.checked_in(conn.id)
telemetry = self._telemetry
if telemetry._should_publish or (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This logic should also be in _telemetry.py, perhaps as a property on the class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same here as above, inlined intentionally for performance.

checkout_started_time = pool._telemetry.checkout_started()
telemetry = pool._telemetry
# Fast path: skip telemetry calls when CMAP events/logging are disabled
if not telemetry._should_publish and not (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also could be factored into _telemetry.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same here as well.

ss.started()
# Server selection does not have APM events, gate only on logging
ss: Optional[_ServerSelectionTelemetry] = None
if _SERVER_SELECTION_LOGGER.isEnabledFor(logging.DEBUG):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also can be factored into _telemetry.py

server.description.address[0],
server.description.address[1],
)
if _SERVER_SELECTION_LOGGER.isEnabledFor(logging.DEBUG):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here, we shouldn't need to have this type of logic in multiple places.

}
op_id = _randint()
client = self.collection.database.client
op_id = _randint() if _should_generate_op_id(client._event_listeners) else None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe this function should either return a _randint or None itself

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