Skip to content

Psycopg instrumentation errors when retrieving libpq build version #3793

@spwoodcock

Description

@spwoodcock

Describe your environment

OS: Debian Trixie (13)
Python version: 3.13
Package version: 0.58b0

What happened?

I was trying to hunt down the source of a mysterious error on my server startup.
I finally narrowed it down to opentelemetry-instrumentation-psycopg, with error:

  File "/opt/python/lib/python3.13/site-packages/opentelemetry/instrumentation/dbapi/__init__.py", line 317, in __init__
    self.commenter_data = self.calculate_commenter_data()
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/python/lib/python3.13/site-packages/opentelemetry/instrumentation/dbapi/__init__.py", line 356, in calculate_commenter_data
    libpq_version = self.connect_module.pq.__build_version__
                    ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'AsyncConnection' has no attribute 'pq'

The relevant part of code is:

if self.database_system == "postgresql":
if hasattr(self.connect_module, "__libpq_version__"):
libpq_version = self.connect_module.__libpq_version__
else:
libpq_version = self.connect_module.pq.__build_version__
commenter_data.update({"libpq_version": libpq_version})

I'm not sure if either:

  • psycopg (3) doesn't correctly implement the pg attribute of the AsyncConnector class, or
  • .pg isn't valid in the Python DBAPI 2.0 spec

psycopg2 previously had libpq_version to access on the connection object, so this worked fine.

psycopg has a few equivalent attributes on the top level module, instead of the connection object.

Steps to Reproduce

As part of FastAPI web server:

database.py

from typing import Union
import logging
import psycopg


log = logging.getLogger(__name__)


def create_connection(db: Union[str, psycopg.Connection]) -> psycopg.Connection:
    """Get db connection from existing psycopg connection, or URL string.

    Args:
        db (str, psycopg.Connection):
            string or existing db connection.
            If `db` is a string, a new connection is generated.
            If `db` is a psycopg connection, the connection is reused.

    Returns:
        conn: DBAPI connection object to generate cursors from.
    """
    if isinstance(db, psycopg.Connection):
        conn = db
    elif isinstance(db, str):
        conn = psycopg.connect(db)
    else:
        msg = "The `db` variable is not a valid string or psycopg connection."
        log.error(msg)
        raise ValueError(msg)

    return conn

main.py

@asynccontextmanager
async def lifespan(
    app: FastAPI,  # dead: disable
) -> AsyncIterator[None]:
    """FastAPI startup/shutdown event."""
    log.debug("Starting up FastAPI server")

    # Create a pooled db connection and make available in lifespan state
    # https://asgi.readthedocs.io/en/latest/specs/lifespan.html#lifespan-state
    # NOTE to use within a request (this is wrapped in database.py already):
    # from typing import cast
    # db_pool = cast(AsyncConnectionPool, request.state.db_pool)
    # async with db_pool.connection() as conn:
    db_pool = get_db_connection_pool()
    log.debug("Opening database connection pool for server")
    await db_pool.open()
    log.debug("Database pool opened, attaching to lifespan")

    yield {"db_pool": db_pool}

    # Shutdown events
    log.debug("Shutting down FastAPI server")
    await db_pool.close()

Expected Result

  • The instrumentation should either succeed connecting to the database driver, else fail gracefully.
  • There should be some error handling here at the very least, and perhaps a warning log.

Actual Result

  • Currently the error is not caught, so it halts server startup and causes an infinite loop.

Additional context

No response

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.

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