diff --git a/docs/env.md b/docs/env.md index 2dad89ec..0441b2ab 100644 --- a/docs/env.md +++ b/docs/env.md @@ -41,7 +41,8 @@ These are the environment variables you can set for the `impress-backend` contai | CONVERSION_FILE_MAX_SIZE | The file max size allowed when uploaded to convert it | 20971520 (20MB) | | CONVERSION_FILE_EXTENSIONS_ALLOWED | Extension list managed by the conversion service | [".docx", ".md"] | | CRISP_WEBSITE_ID | Crisp website id for support | | -| DB_ENGINE | Engine to use for database connections | django.db.backends.postgresql_psycopg2 | +| DB_CONN_MAX_AGE | Maximum lifetime of a database connection in seconds. Use 0 to close connections at the end of each request (default). When using the psycopg pool, set to 0 so connections are returned to the pool after each request. | 0 | +| DB_ENGINE | Engine to use for database connections | django.db.backends.postgresql | | DB_HOST | Host of the database | localhost | | DB_NAME | Name of the database | impress | | DB_PASSWORD | Password to authenticate with | pass | diff --git a/src/backend/core/tests/test_settings.py b/src/backend/core/tests/test_settings.py index 44c2a4ce..ed786de3 100644 --- a/src/backend/core/tests/test_settings.py +++ b/src/backend/core/tests/test_settings.py @@ -30,6 +30,20 @@ def test_invalid_settings_oidc_email_configuration(): ) +def test_settings_conn_max_age_default(): + """ + Test that DB_CONN_MAX_AGE defaults to 0 (close connections at the end of each request). + + CONN_MAX_AGE is defined in the DATABASES class body and resolved by django-configurations + at class setup time, so no post_setup() call is required here. + """ + + class TestSettings(Base): + """Fake test settings.""" + + assert TestSettings.DATABASES["default"]["CONN_MAX_AGE"] == 0 + + def test_settings_psycopg_pool_not_enabled(): """ Test that not changing DB_PSYCOPG_POOL_ENABLED should not configure psycopg in the DATABASES diff --git a/src/backend/impress/settings.py b/src/backend/impress/settings.py index aea0b45d..ba4354d3 100755 --- a/src/backend/impress/settings.py +++ b/src/backend/impress/settings.py @@ -99,6 +99,15 @@ class Base(Configuration): "localhost", environ_name="DB_HOST", environ_prefix=None ), "PORT": values.Value(5432, environ_name="DB_PORT", environ_prefix=None), + # Maximum lifetime of a database connection in seconds. + # Use 0 to close connections at the end of each request. + # Use None for unlimited persistent connections. + # When using the psycopg pool (DB_PSYCOPG_POOL_ENABLED), set this to 0 + # so that connections are returned to the pool after each request. + "CONN_MAX_AGE": values.IntegerValue( + 0, environ_name="DB_CONN_MAX_AGE", environ_prefix=None + ), + "OPTIONS": {}, # Psycopg pool can be configured in the post_setup method } } @@ -1113,30 +1122,24 @@ class Base(Configuration): ) if psycopg_pool_enabled: - cls.DATABASES["default"].update( - { - "OPTIONS": { - # https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool - "pool": { - "min_size": values.IntegerValue( - 4, - environ_name="DB_PSYCOPG_POOL_MIN_SIZE", - environ_prefix=None, - ), - "max_size": values.IntegerValue( - None, - environ_name="DB_PSYCOPG_POOL_MAX_SIZE", - environ_prefix=None, - ), - "timeout": values.IntegerValue( - 3, - environ_name="DB_PSYCOPG_POOL_TIMEOUT", - environ_prefix=None, - ), - } - }, - } - ) + # https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool + cls.DATABASES["default"].setdefault("OPTIONS", {})["pool"] = { + "min_size": values.IntegerValue( + 4, + environ_name="DB_PSYCOPG_POOL_MIN_SIZE", + environ_prefix=None, + ), + "max_size": values.IntegerValue( + None, + environ_name="DB_PSYCOPG_POOL_MAX_SIZE", + environ_prefix=None, + ), + "timeout": values.IntegerValue( + 3, + environ_name="DB_PSYCOPG_POOL_TIMEOUT", + environ_prefix=None, + ), + } class Build(Base):