Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Properly closed cache connections at the end of the request.

This only affects the new cache api and not the deprecated get_cache.

Refs #21012
  • Loading branch information...
commit d47f794f8fa05591632b8cad4b134858e7ae140d 1 parent 8adbfdf
Florian Apolloner authored November 24, 2013
32  django/core/cache/__init__.py
@@ -53,7 +53,12 @@ def get_cache(backend, **kwargs):
53 53
     """
54 54
     warnings.warn("'get_cache' is deprecated in favor of 'caches'.",
55 55
                   PendingDeprecationWarning, stacklevel=2)
56  
-    return _create_cache(backend, **kwargs)
  56
+    cache = _create_cache(backend, **kwargs)
  57
+    # Some caches -- python-memcached in particular -- need to do a cleanup at the
  58
+    # end of a request cycle. If not implemented in a particular backend
  59
+    # cache.close is a no-op
  60
+    signals.request_finished.connect(cache.close)
  61
+    return cache
57 62
 
58 63
 
59 64
 def _create_cache(backend, **kwargs):
@@ -79,12 +84,7 @@ def _create_cache(backend, **kwargs):
79 84
     except (AttributeError, ImportError, ImproperlyConfigured) as e:
80 85
         raise InvalidCacheBackendError(
81 86
             "Could not find backend '%s': %s" % (backend, e))
82  
-    cache = backend_cls(location, params)
83  
-    # Some caches -- python-memcached in particular -- need to do a cleanup at the
84  
-    # end of a request cycle. If not implemented in a particular backend
85  
-    # cache.close is a no-op
86  
-    signals.request_finished.connect(cache.close)
87  
-    return cache
  87
+    return backend_cls(location, params)
88 88
 
89 89
 
90 90
 class CacheHandler(object):
@@ -98,8 +98,10 @@ def __init__(self):
98 98
 
99 99
     def __getitem__(self, alias):
100 100
         try:
101  
-            return getattr(self._caches, alias)
  101
+            return self._caches.caches[alias]
102 102
         except AttributeError:
  103
+            self._caches.caches = {}
  104
+        except KeyError:
103 105
             pass
104 106
 
105 107
         if alias not in settings.CACHES:
@@ -108,10 +110,12 @@ def __getitem__(self, alias):
108 110
             )
109 111
 
110 112
         cache = _create_cache(alias)
111  
-        setattr(self._caches, alias, cache)
112  
-
  113
+        self._caches.caches[alias] = cache
113 114
         return cache
114 115
 
  116
+    def all(self):
  117
+        return getattr(self._caches, 'caches', {}).values()
  118
+
115 119
 caches = CacheHandler()
116 120
 
117 121
 
@@ -141,3 +145,11 @@ def __ne__(self, other):
141 145
         return caches[DEFAULT_CACHE_ALIAS] != other
142 146
 
143 147
 cache = DefaultCacheProxy()
  148
+
  149
+def close_caches(**kwargs):
  150
+    # Some caches -- python-memcached in particular -- need to do a cleanup at the
  151
+    # end of a request cycle. If not implemented in a particular backend
  152
+    # cache.close is a no-op
  153
+    for cache in caches.all():
  154
+        cache.close()
  155
+signals.request_finished.connect(close_caches)
20  tests/cache/tests.py
@@ -203,7 +203,6 @@ def custom_key_func(key, key_prefix, version):
203 203
     'custom_key2': {'KEY_FUNCTION': 'cache.tests.custom_key_func'},
204 204
     'cull': {'OPTIONS': {'MAX_ENTRIES': 30}},
205 205
     'zero_cull': {'OPTIONS': {'CULL_FREQUENCY': 0, 'MAX_ENTRIES': 30}},
206  
-    'other': {'LOCATION': 'other'},
207 206
 }
208 207
 
209 208
 
@@ -1014,6 +1013,13 @@ def setUp(self):
1014 1013
         caches['custom_key2']._cache = cache._cache
1015 1014
         caches['custom_key2']._expire_info = cache._expire_info
1016 1015
 
  1016
+    @override_settings(CACHES={
  1017
+        'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'},
  1018
+        'other': {
  1019
+            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
  1020
+            'LOCATION': 'other'
  1021
+        },
  1022
+    })
1017 1023
     def test_multiple_caches(self):
1018 1024
         "Check that multiple locmem caches are isolated"
1019 1025
         cache.set('value', 42)
@@ -1139,6 +1145,12 @@ def test_custom_key_validation(self):
1139 1145
         self.assertEqual(cache.get(key), val)
1140 1146
 
1141 1147
 
  1148
+@override_settings(
  1149
+    CACHES={
  1150
+            'default': {
  1151
+                'BACKEND': 'cache.closeable_cache.CacheClass',
  1152
+            }
  1153
+        },)
1142 1154
 class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase):
1143 1155
 
1144 1156
     def test_simple(self):
@@ -1157,6 +1169,12 @@ def test_simple(self):
1157 1169
         self.assertRaises(InvalidCacheBackendError, get_cache, 'does_not_exist')
1158 1170
 
1159 1171
     def test_close(self):
  1172
+        from django.core import signals
  1173
+        self.assertFalse(cache.closed)
  1174
+        signals.request_finished.send(self.__class__)
  1175
+        self.assertTrue(cache.closed)
  1176
+
  1177
+    def test_close_deprecated(self):
1160 1178
         from django.core.cache import get_cache
1161 1179
         from django.core import signals
1162 1180
         cache = get_cache('cache.closeable_cache.CacheClass')

0 notes on commit d47f794

Please sign in to comment.
Something went wrong with that request. Please try again.