From 4f92cf87b013801810226928ddd20097f6e4fccf Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Tue, 8 Mar 2022 11:48:56 +0100 Subject: [PATCH] Prevented initialization of unused database connections. --- django/contrib/postgres/apps.py | 2 +- django/core/cache/__init__.py | 8 -------- django/core/handlers/base.py | 9 +++------ django/db/__init__.py | 4 ++-- django/test/signals.py | 2 +- django/test/testcases.py | 4 ++-- django/utils/connection.py | 9 +++++++-- tests/utils_tests/test_connection.py | 4 ++++ 8 files changed, 20 insertions(+), 22 deletions(-) diff --git a/django/contrib/postgres/apps.py b/django/contrib/postgres/apps.py index d917201f05..79fbe57c8f 100644 --- a/django/contrib/postgres/apps.py +++ b/django/contrib/postgres/apps.py @@ -51,7 +51,7 @@ class PostgresConfig(AppConfig): def ready(self): setting_changed.connect(uninstall_if_needed) # Connections may already exist before we are called. - for conn in connections.all(): + for conn in connections.all(initialized_only=True): if conn.vendor == "postgresql": conn.introspection.data_types_reverse.update( { diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py index f09c9ecc4b..613d3632ed 100644 --- a/django/core/cache/__init__.py +++ b/django/core/cache/__init__.py @@ -51,14 +51,6 @@ class CacheHandler(BaseConnectionHandler): ) from e return backend_cls(location, params) - def all(self, initialized_only=False): - return [ - self[alias] - for alias in self - # If initialized_only is True, return only initialized caches. - if not initialized_only or hasattr(self._connections, alias) - ] - caches = CacheHandler() diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index caee168cbf..a934659186 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -344,16 +344,13 @@ class BaseHandler: def make_view_atomic(self, view): non_atomic_requests = getattr(view, "_non_atomic_requests", set()) - for db in connections.all(): - if ( - db.settings_dict["ATOMIC_REQUESTS"] - and db.alias not in non_atomic_requests - ): + for alias, settings_dict in connections.settings.items(): + if settings_dict["ATOMIC_REQUESTS"] and alias not in non_atomic_requests: if asyncio.iscoroutinefunction(view): raise RuntimeError( "You cannot use ATOMIC_REQUESTS with async views." ) - view = transaction.atomic(using=db.alias)(view) + view = transaction.atomic(using=alias)(view) return view def process_exception_by_middleware(self, exception, request): diff --git a/django/db/__init__.py b/django/db/__init__.py index b0cae97e01..f3cf4574a9 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -43,7 +43,7 @@ connection = ConnectionProxy(connections, DEFAULT_DB_ALIAS) # Register an event to reset saved queries when a Django request is started. def reset_queries(**kwargs): - for conn in connections.all(): + for conn in connections.all(initialized_only=True): conn.queries_log.clear() @@ -53,7 +53,7 @@ signals.request_started.connect(reset_queries) # Register an event to reset transaction state and close connections past # their lifetime. def close_old_connections(**kwargs): - for conn in connections.all(): + for conn in connections.all(initialized_only=True): conn.close_if_unusable_or_obsolete() diff --git a/django/test/signals.py b/django/test/signals.py index c874f220df..75b6339a15 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -70,7 +70,7 @@ def update_connections_time_zone(*, setting, **kwargs): # Reset the database connections' time zone if setting in {"TIME_ZONE", "USE_TZ"}: - for conn in connections.all(): + for conn in connections.all(initialized_only=True): try: del conn.timezone except AttributeError: diff --git a/django/test/testcases.py b/django/test/testcases.py index 3758671117..bfcfef58cb 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -1197,7 +1197,7 @@ class TransactionTestCase(SimpleTestCase): # tests (e.g., losing a timezone setting causing objects to be # created with the wrong time). To make sure this doesn't # happen, get a clean connection at the start of every test. - for conn in connections.all(): + for conn in connections.all(initialized_only=True): conn.close() finally: if self.available_apps is not None: @@ -1378,7 +1378,7 @@ class TestCase(TransactionTestCase): def tearDownClass(cls): if cls._databases_support_transactions(): cls._rollback_atomics(cls.cls_atomics) - for conn in connections.all(): + for conn in connections.all(initialized_only=True): conn.close() super().tearDownClass() diff --git a/django/utils/connection.py b/django/utils/connection.py index 1b5895e8d3..5a82769cfc 100644 --- a/django/utils/connection.py +++ b/django/utils/connection.py @@ -72,5 +72,10 @@ class BaseConnectionHandler: def __iter__(self): return iter(self.settings) - def all(self): - return [self[alias] for alias in self] + def all(self, initialized_only=False): + return [ + self[alias] + for alias in self + # If initialized_only is True, return only initialized connections. + if not initialized_only or hasattr(self._connections, alias) + ] diff --git a/tests/utils_tests/test_connection.py b/tests/utils_tests/test_connection.py index e7d63b8d0f..e1e2bbb28c 100644 --- a/tests/utils_tests/test_connection.py +++ b/tests/utils_tests/test_connection.py @@ -8,3 +8,7 @@ class BaseConnectionHandlerTests(SimpleTestCase): msg = "Subclasses must implement create_connection()." with self.assertRaisesMessage(NotImplementedError, msg): handler.create_connection(None) + + def test_all_initialized_only(self): + handler = BaseConnectionHandler({"default": {}}) + self.assertEqual(handler.all(initialized_only=True), [])