From 958cdf65ae90d26236d1815bbba804729595ec7a Mon Sep 17 00:00:00 2001
From: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Date: Fri, 14 May 2021 11:53:17 +0200
Subject: [PATCH] Fixed #32747 -- Prevented initialization of unused caches.

Thanks Alexander Ebral for the report.

Regression in 98e05ccde440cc9b768952cc10bc8285f4924e1f.
---
 django/core/cache/__init__.py |  9 ++++++++-
 docs/releases/3.2.4.txt       |  3 +++
 tests/cache/tests.py          | 30 ++++++++++++++++++++++++++++++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py
index 05ef3897d0..a311b50af6 100644
--- a/django/core/cache/__init__.py
+++ b/django/core/cache/__init__.py
@@ -43,6 +43,13 @@ 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()
 
@@ -52,7 +59,7 @@ cache = ConnectionProxy(caches, DEFAULT_CACHE_ALIAS)
 def close_caches(**kwargs):
     # Some caches need to do a cleanup at the end of a request cycle. If not
     # implemented in a particular backend cache.close() is a no-op.
-    for cache in caches.all():
+    for cache in caches.all(initialized_only=True):
         cache.close()
 
 
diff --git a/docs/releases/3.2.4.txt b/docs/releases/3.2.4.txt
index 068798e6ed..dac726e961 100644
--- a/docs/releases/3.2.4.txt
+++ b/docs/releases/3.2.4.txt
@@ -15,3 +15,6 @@ Bugfixes
 
 * Fixed a bug in Django 3.2 where a system check would crash on an abstract
   model (:ticket:`32733`).
+
+* Prevented unnecessary initialization of unused caches following a regression
+  in Django 3.2 (:ticket:`32747`).
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index c189e26e70..65a8f12fc7 100644
--- a/tests/cache/tests.py
+++ b/tests/cache/tests.py
@@ -1723,6 +1723,19 @@ class CacheClosingTests(SimpleTestCase):
         signals.request_finished.send(self.__class__)
         self.assertTrue(cache.closed)
 
+    def test_close_only_initialized(self):
+        with self.settings(CACHES={
+            'cache_1': {
+                'BACKEND': 'cache.closeable_cache.CacheClass',
+            },
+            'cache_2': {
+                'BACKEND': 'cache.closeable_cache.CacheClass',
+            },
+        }):
+            self.assertEqual(caches.all(initialized_only=True), [])
+            signals.request_finished.send(self.__class__)
+            self.assertEqual(caches.all(initialized_only=True), [])
+
 
 DEFAULT_MEMORY_CACHES_SETTINGS = {
     'default': {
@@ -2625,3 +2638,20 @@ class CacheHandlerTest(SimpleTestCase):
         )
         with self.assertRaisesMessage(InvalidCacheBackendError, msg):
             test_caches['invalid_backend']
+
+    def test_all(self):
+        test_caches = CacheHandler({
+            'cache_1': {
+                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+            },
+            'cache_2': {
+                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+            },
+        })
+        self.assertEqual(test_caches.all(initialized_only=True), [])
+        cache_1 = test_caches['cache_1']
+        self.assertEqual(test_caches.all(initialized_only=True), [cache_1])
+        self.assertEqual(len(test_caches.all()), 2)
+        # .all() initializes all caches.
+        self.assertEqual(len(test_caches.all(initialized_only=True)), 2)
+        self.assertEqual(test_caches.all(), test_caches.all(initialized_only=True))