Skip to content

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

Description

@jameswatters1

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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions