#37500 new Cleanup/optimization

Include the database alias in connection-failure error messages

Reported by: Abdul Qoyyuum Owned by:
Component: Database layer (models, ORM) Version: 6.1
Severity: Low Keywords: databases, error messages, multiple databases
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Description

When a project configures more than one entry in DATABASES, a failed
connection raises django.db.utils.OperationalError whose message comes
straight from the database driver and only mentions host and port:

django.db.utils.OperationalError: connection failed: connection to server
    at "127.0.0.1", port 5432 failed: Connection refused

Nothing in the exception identifies which database alias failed
(default vs any other entry in DATABASES), nor the configured NAME,
USER, or HOST for that alias. In a multi-database setup the same
host:port may be correct for one alias and wrong for another, so the reader
cannot tell which connection string was actually attempted. This is
especially painful when host/credentials come from environment variables —
a stale or overridden *_HOST can silently point default at localhost
while a second alias points at a remote server, and the error gives no hint
which of the two failed.

Reproduction

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "app",
        "USER": "app",
        "HOST": "localhost",   # nothing listening here
        "PORT": 5432,
    },
    "ecomdb": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "etx_ecom",
        "USER": "report_api",
        "HOST": "192.168.80.72",
        "PORT": 5432,
    },
}

User.objects.get_by_natural_key("alice")        # uses 'default'
EcomRecords.objects.using("ecomdb").exists()    # uses 'ecomdb'

Whichever connection fails, the raised message has the same shape; the only
way to know which alias was involved is to read the traceback back to the
call site and cross-check connections[alias].settings_dict manually.

Proposal

Enrich the error as it propagates out of
BaseDatabaseWrapper.ensure_connection() / connect() /
get_new_connection() so the driver error is reported together with the
connection alias and the key settings for that alias, e.g.:

django.db.utils.OperationalError: could not connect to database 'ecomdb'
    (postgresql 'etx_ecom' at 192.168.80.72:5432 as 'report_api'):
    connection failed: connection to server at "127.0.0.1",
    port 5432 failed: Connection refused

An alternative or complementary approach is to attach the context
programmatically instead of only in the message text — e.g.
BaseException.add_note() (Python 3.11+) or an attribute such as
exc.django_connection_alias = self.alias — so that logging,
error-reporting tools, and tests can pick the alias up without parsing the
message.

Notes

  • django.db.backends.signals.connection_created exists for successful connections, but there is no equivalent signal or hook on failure. Today the only way to add the alias is to subclass each backend's DatabaseWrapper under a custom ENGINE path — which also means the workaround must be re-implemented per third-party backend rather than once in Django itself.
  • Care should be taken not to include the connection PASSWORD in the enriched message; NAME/USER/HOST/PORT are already visible in settings_dict and match what drivers print today.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top