Search code examples
djangopostgresql

When does Djangos CONN_MAX_AGE get checked/used?


I recently had a "Too many connections" issues of my Django+Celery application with a Postgres DB on Heroku.

Could dyno restarts be the issue? The idea is that the dyno restart drops the connection, but Postgres keeps them.

Setting CONN_MAX_AGE seems to have solved the issue, but I'm uncertain.

The question now is: How does CONN_MAX_AGE work?

(1) Does Django actively check the age of the connection and close + restart it when it's too old

or

(2) Is CONN_MAX_AGE a parameter of the connection itself, so that postgres automatically closes the connection after that time?


Solution

  • Does Django actively check the age of the connection and close + restart it when it's too old.

    Django will, each time it opens a connection, set the time when it has to close the connection [GitHub]:

    class BaseDatabaseWrapper:
        # …
        @async_unsafe
        def connect(self):
            # …
            max_age = self.settings_dict["CONN_MAX_AGE"]
            self.close_at = None if max_age is None else time.monotonic() + max_age

    Django will then, when a method named .close_if_unusable_or_obsolete() is called, close the connection [GitHub]:

    class BaseDatabaseWrapper:
        # …
        def close_if_unusable_or_obsolete(self):
            # …
            if self.connection is not None:
                # …
                if self.close_at is not None and time.monotonic() >= self.close_at:
                    self.close()
                    return

    You can call that method yourself to "recycle" connections. But it is by default done when a request starts and when a request ends, indeed Django registers this on two signals [GitHub]:

    def
    close_old_connections(**kwargs):
        for conn in connections.all(initialized_only=True):
            conn.close_if_unusable_or_obsolete()
    
    
    signals.request_started.connect(close_old_connections)
    signals.request_finished.connect(close_old_connections)

    It will thus, before a request fires, recycle the connections, and also after a request has fired, recycle the connections with unrecoverable errors, or outdated ones. Recycling these in the middle of a request might not be a good idea, since the connection might be stateful.