diff --git a/authentik/lib/config.py b/authentik/lib/config.py index 2581dd038a..dc940b108d 100644 --- a/authentik/lib/config.py +++ b/authentik/lib/config.py @@ -356,6 +356,14 @@ def redis_url(db: int) -> str: def django_db_config(config: ConfigLoader | None = None) -> dict: if not config: config = CONFIG + + pool_options = False + use_pool = config.get_bool("postgresql.use_pool", False) + if use_pool: + pool_options = config.get_dict_from_b64_json("postgresql.pool_options", True) + if not pool_options: + pool_options = True + db = { "default": { "ENGINE": "authentik.root.db", @@ -369,6 +377,7 @@ def django_db_config(config: ConfigLoader | None = None) -> dict: "sslrootcert": config.get("postgresql.sslrootcert"), "sslcert": config.get("postgresql.sslcert"), "sslkey": config.get("postgresql.sslkey"), + "pool": pool_options, }, "CONN_MAX_AGE": config.get_optional_int("postgresql.conn_max_age", 0), "CONN_HEALTH_CHECKS": config.get_bool("postgresql.conn_health_checks", False), diff --git a/authentik/lib/default.yml b/authentik/lib/default.yml index 6327996a22..d6f3579055 100644 --- a/authentik/lib/default.yml +++ b/authentik/lib/default.yml @@ -21,6 +21,7 @@ postgresql: user: authentik port: 5432 password: "env://POSTGRES_PASSWORD" + use_pool: False test: name: test_authentik default_schema: public diff --git a/authentik/lib/tests/test_config.py b/authentik/lib/tests/test_config.py index df0ec526aa..92a03534c7 100644 --- a/authentik/lib/tests/test_config.py +++ b/authentik/lib/tests/test_config.py @@ -217,6 +217,7 @@ class TestConfig(TestCase): "HOST": "foo", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -267,6 +268,7 @@ class TestConfig(TestCase): "HOST": "foo", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -285,6 +287,7 @@ class TestConfig(TestCase): "HOST": "bar", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -333,6 +336,7 @@ class TestConfig(TestCase): "HOST": "foo", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -351,6 +355,7 @@ class TestConfig(TestCase): "HOST": "bar", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -394,6 +399,7 @@ class TestConfig(TestCase): "HOST": "foo", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -412,6 +418,7 @@ class TestConfig(TestCase): "HOST": "bar", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -451,6 +458,7 @@ class TestConfig(TestCase): "HOST": "foo", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "foo", "sslkey": "foo", "sslmode": "foo", @@ -469,6 +477,7 @@ class TestConfig(TestCase): "HOST": "bar", "NAME": "foo", "OPTIONS": { + "pool": False, "sslcert": "bar", "sslkey": "foo", "sslmode": "foo", @@ -484,3 +493,87 @@ class TestConfig(TestCase): }, }, ) + + def test_db_pool(self): + """Test DB Config with pool""" + config = ConfigLoader() + config.set("postgresql.host", "foo") + config.set("postgresql.name", "foo") + config.set("postgresql.user", "foo") + config.set("postgresql.password", "foo") + config.set("postgresql.port", "foo") + config.set("postgresql.test.name", "foo") + config.set("postgresql.use_pool", True) + conf = django_db_config(config) + self.assertEqual( + conf, + { + "default": { + "ENGINE": "authentik.root.db", + "HOST": "foo", + "NAME": "foo", + "OPTIONS": { + "pool": True, + "sslcert": None, + "sslkey": None, + "sslmode": None, + "sslrootcert": None, + }, + "PASSWORD": "foo", + "PORT": "foo", + "TEST": {"NAME": "foo"}, + "USER": "foo", + "CONN_MAX_AGE": 0, + "CONN_HEALTH_CHECKS": False, + "DISABLE_SERVER_SIDE_CURSORS": False, + } + }, + ) + + def test_db_pool_options(self): + """Test DB Config with pool""" + config = ConfigLoader() + config.set("postgresql.host", "foo") + config.set("postgresql.name", "foo") + config.set("postgresql.user", "foo") + config.set("postgresql.password", "foo") + config.set("postgresql.port", "foo") + config.set("postgresql.test.name", "foo") + config.set("postgresql.use_pool", True) + config.set( + "postgresql.pool_options", + base64.b64encode( + dumps( + { + "max_size": 15, + } + ).encode() + ).decode(), + ) + conf = django_db_config(config) + self.assertEqual( + conf, + { + "default": { + "ENGINE": "authentik.root.db", + "HOST": "foo", + "NAME": "foo", + "OPTIONS": { + "pool": { + "max_size": 15, + }, + "sslcert": None, + "sslkey": None, + "sslmode": None, + "sslrootcert": None, + }, + "PASSWORD": "foo", + "PORT": "foo", + "TEST": {"NAME": "foo"}, + "USER": "foo", + "CONN_MAX_AGE": 0, + "CONN_HEALTH_CHECKS": False, + "DISABLE_SERVER_SIDE_CURSORS": False, + } + }, + ) diff --git a/pyproject.toml b/pyproject.toml index 521fd22c02..cc7c001d28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ dependencies = [ "opencontainers", "packaging", "paramiko", - "psycopg[c]", + "psycopg[c, pool]", "pydantic", "pydantic-scim", "pyjwt", diff --git a/uv.lock b/uv.lock index 75b6aad1f2..667c9224a7 100644 --- a/uv.lock +++ b/uv.lock @@ -210,7 +210,7 @@ dependencies = [ { name = "opencontainers" }, { name = "packaging" }, { name = "paramiko" }, - { name = "psycopg", extra = ["c"] }, + { name = "psycopg", extra = ["c", "pool"] }, { name = "pydantic" }, { name = "pydantic-scim" }, { name = "pyjwt" }, @@ -308,7 +308,7 @@ requires-dist = [ { name = "opencontainers", git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8" }, { name = "packaging" }, { name = "paramiko" }, - { name = "psycopg", extras = ["c"] }, + { name = "psycopg", extras = ["c", "pool"] }, { name = "pydantic" }, { name = "pydantic-scim" }, { name = "pyjwt" }, @@ -2321,6 +2321,9 @@ wheels = [ c = [ { name = "psycopg-c", marker = "implementation_name != 'pypy'" }, ] +pool = [ + { name = "psycopg-pool" }, +] [[package]] name = "psycopg-c" @@ -2328,6 +2331,18 @@ version = "3.2.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/2f/f1/367a2429af2b97f6a46dc116206cd3b1cf668fca7ff3c22b979ea0686427/psycopg_c-3.2.6.tar.gz", hash = "sha256:b5fd4ce70f82766a122ca5076a36c4d5818eaa9df9bf76870bc83a064ffaed3a", size = 609304 } +[[package]] +name = "psycopg-pool" +version = "3.2.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/13/1e7850bb2c69a63267c3dbf37387d3f71a00fd0e2fa55c5db14d64ba1af4/psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5", size = 29770 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/fd/4feb52a55c1a4bd748f2acaed1903ab54a723c47f6d0242780f4d97104d4/psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7", size = 38252 }, +] + [[package]] name = "publication" version = "0.0.3" diff --git a/website/docs/releases/2025/v2025.4.md b/website/docs/releases/2025/v2025.4.md index c135ddb4f4..03da72186c 100644 --- a/website/docs/releases/2025/v2025.4.md +++ b/website/docs/releases/2025/v2025.4.md @@ -19,6 +19,8 @@ Previously, sessions were stored by default in the cache. Now, they are stored i ## New features +### Postgres pool + ## Upgrading This release does not introduce any new requirements. You can follow the upgrade instructions below; for more detailed information about upgrading authentik, refer to our [Upgrade documentation](../../install-config/upgrade.mdx).