open-telemetry / open-telemetry/opentelemetry-python-contrib

traced_execution_async` in `opentelemetry-instrumentation-dbapi` ignores instrumentation suppression

Open
#4,981 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1.1k
Forks
1.1k
Avg merge
4d 15h
Merged PRs (30d)
16

Description

Describe your environment
  • opentelemetry-instrumentation-dbapi 0.62b1
  • opentelemetry-instrumentation-psycopg 0.62b1
  • psycopg 3 with AsyncConnection / AsyncConnectionPool
  • Python 3.14
What happened?

Spans are still created (and exported) for queries executed inside
opentelemetry.instrumentation.utils.suppress_instrumentation() when the
cursor is async. The sync path correctly suppresses.

Steps to Reproduce
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor
from opentelemetry.instrumentation.utils import suppress_instrumentation
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

from psycopg import AsyncConnection

exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
PsycopgInstrumentor().instrument(tracer_provider=provider)

async def main():
    async with await AsyncConnection.connect(DSN) as conn:
        async with conn.cursor() as cur:
            with suppress_instrumentation():
                await cur.execute("SELECT 1")

# after running main():
assert exporter.get_finished_spans() == ()   # FAILS — one span was exported

The same code with a sync psycopg.connect() passes.

Expected Result

No span — the async path should honor suppression exactly as the sync path
does.

Actual Result

One span was created and exported for the SELECT 1 executed inside suppress_instrumentation() — exporter.get_finished_spans() returns a span named SELECT with db.statement = "SELECT 1", and the final assertion in the reproducer fails.
Running the identical code with a sync psycopg.connect() connection produces no span — the sync path honors suppression, the async path does not.

Additional context

The common use of suppress_instrumentation() around background polls
(outbox sweeps, health/metric COUNT queries) silently stops working the day a
codebase migrates from sync to async connections — every poll becomes a root
span again, with no error and no signal that suppression is inert. Tests that
assert is_instrumentation_enabled() inside the block keep passing, because
the flag is set correctly; it is simply never read.

Would you like to implement a fix?

None

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in the opentelemetry-instrumentation-dbapi async cursor execution path and compare it with the sync path, using the provided psycopg AsyncConnection reproducer. Verify the change by checking that the suppression block produces no finished spans, while the existing sync behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
observability
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.