From f19d4970dcad2216c373c0b1f8f9da5c006afc6b Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 11 Dec 2022 15:38:29 +0000 Subject: [PATCH] Add ability to set CONN_HEALTH_CHECKS (#185) --- README.rst | 100 +++++++++++++++++++++++++++++++--------- dj_database_url.py | 5 +- test_dj_database_url.py | 18 ++++++++ 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index 7eb12bd..71f0c74 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,9 @@ Oracle, Oracle (GIS), Redshift, CockroachDB, Timescale, Timescale (GIS) and SQLi Installation ------------ -Installation is simple:: +Installation is simple: + +.. code-block:: console $ pip install dj-database-url @@ -40,48 +42,104 @@ Usage 1. If ``DATABASES`` is already defined: -- Configure your database in ``settings.py`` from ``DATABASE_URL``:: +- Configure your database in ``settings.py`` from ``DATABASE_URL``: + + .. code-block:: python + + import dj_database_url - import dj_database_url + DATABASES['default'] = dj_database_url.config( + conn_max_age=600, + conn_health_checks=True, + ) - DATABASES['default'] = dj_database_url.config(conn_max_age=600) +- Provide a default: -- Provide a default:: + .. code-block:: python - DATABASES['default'] = dj_database_url.config(default='postgres://...') + DATABASES['default'] = dj_database_url.config( + default='postgres://...', + conn_max_age=600, + conn_health_checks=True, + ) -- Parse an arbitrary Database URL:: +- Parse an arbitrary Database URL: - DATABASES['default'] = dj_database_url.parse('postgres://...', conn_max_age=600) + .. code-block:: python + + DATABASES['default'] = dj_database_url.parse( + 'postgres://...', + conn_max_age=600, + conn_health_checks=True, + ) 2. If ``DATABASES`` is not defined: -- Configure your database in ``settings.py`` from ``DATABASE_URL``:: +- Configure your database in ``settings.py`` from ``DATABASE_URL``: + + .. code-block:: python + + import dj_database_url - import dj_database_url + DATABASES = { + 'default': dj_database_url.config( + conn_max_age=600, + conn_health_checks=True, + ), + } - DATABASES = {'default': dj_database_url.config(conn_max_age=600)} +- You can provide a default, used if the ``DATABASE_URL`` setting is not defined: -- Provide a default:: + .. code-block:: python - DATABASES = {'default': dj_database_url.config(default='postgres://...')} + DATABASES = { + 'default': dj_database_url.config( + default='postgres://...', + conn_max_age=600, + conn_health_checks=True, + ) + } -- Parse an arbitrary Database URL:: +- Parse an arbitrary Database URL: - DATABASES = {'default': dj_database_url.parse('postgres://...', conn_max_age=600)} + .. code-block:: python -The ``conn_max_age`` attribute is the lifetime of a database connection in seconds -and is available in Django 1.6+. If you do not set a value, it will default to ``0`` -which is Django's historical behavior of using a new database connection on each -request. Use ``None`` for unlimited persistent connections. + DATABASES = { + 'default': dj_database_url.parse( + 'postgres://...', + conn_max_age=600, + conn_health_checks=True, + ) + } + +``conn_max_age`` sets the |CONN_MAX_AGE setting|__, which tells Django to +persist database connections between requests, up to the given lifetime in +seconds. If you do not provide a value, it will follow Django’s default of +``0``. Setting it is recommended for performance. + +.. |CONN_MAX_AGE setting| replace:: ``CONN_MAX_AGE`` setting +__ https://docs.djangoproject.com/en/stable/ref/settings/#conn-max-age + +``conn_health_checks`` sets the |CONN_HEALTH_CHECKS setting|__ (new in Django +4.1), which tells Django to check a persisted connection still works at the +start of each request. If you do not provide a value, it will follow Django’s +default of ``False``. Enabling it is recommended if you set a non-zero +``conn_max_age``. + +.. |CONN_HEALTH_CHECKS setting| replace:: ``CONN_HEALTH_CHECKS`` setting +__ https://docs.djangoproject.com/en/stable/ref/settings/#conn-health-checks Strings passed to `dj_database_url` must be valid URLs; in particular, special characters must be url-encoded. The following url will raise -a `ValueError`:: +a `ValueError`: + +.. code-block:: plaintext postgres://user:p#ssword!@localhost/foobar -and should instead be passed as:: +and should instead be passed as: + +.. code-block:: plaintext postgres://user:p%23ssword!@localhost/foobar diff --git a/dj_database_url.py b/dj_database_url.py index bfba749..286b575 100644 --- a/dj_database_url.py +++ b/dj_database_url.py @@ -57,7 +57,9 @@ def config( return {} -def parse(url, engine=None, conn_max_age=0, ssl_require=False): +def parse( + url, engine=None, conn_max_age=0, conn_health_checks=False, ssl_require=False +): """Parses a database URL.""" if url == "sqlite://:memory:": @@ -115,6 +117,7 @@ def parse(url, engine=None, conn_max_age=0, ssl_require=False): "HOST": hostname, "PORT": port or "", "CONN_MAX_AGE": conn_max_age, + "CONN_HEALTH_CHECKS": conn_health_checks, } ) diff --git a/test_dj_database_url.py b/test_dj_database_url.py index 109159e..077622b 100644 --- a/test_dj_database_url.py +++ b/test_dj_database_url.py @@ -490,6 +490,24 @@ def test_timescalegis_parsing_with_special_characters(self): assert url["PASSWORD"] == "#password" assert url["PORT"] == 5431 + def test_persistent_connection_variables(self): + url = dj_database_url.parse( + "sqlite://myfile.db", conn_max_age=600, conn_health_checks=True + ) + + assert url["CONN_MAX_AGE"] == 600 + assert url["CONN_HEALTH_CHECKS"] is True + + def test_sqlite_memory_persistent_connection_variables(self): + # memory sqlite ignores connection.close(), so persistent connection + # variables aren’t required + url = dj_database_url.parse( + "sqlite://:memory:", conn_max_age=600, conn_health_checks=True + ) + + assert "CONN_MAX_AGE" not in url + assert "CONN_HEALTH_CHECKS" not in url + if __name__ == "__main__": unittest.main()