Commit d47f794f authored by Florian Apolloner's avatar Florian Apolloner
Browse files

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
parent 8adbfdfc
Loading
Loading
Loading
Loading
+22 −10
Original line number Diff line number Diff line
@@ -53,7 +53,12 @@ def get_cache(backend, **kwargs):
    """
    warnings.warn("'get_cache' is deprecated in favor of 'caches'.",
                  PendingDeprecationWarning, stacklevel=2)
    return _create_cache(backend, **kwargs)
    cache = _create_cache(backend, **kwargs)
    # Some caches -- python-memcached in particular -- 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
    signals.request_finished.connect(cache.close)
    return cache


def _create_cache(backend, **kwargs):
@@ -79,12 +84,7 @@ def _create_cache(backend, **kwargs):
    except (AttributeError, ImportError, ImproperlyConfigured) as e:
        raise InvalidCacheBackendError(
            "Could not find backend '%s': %s" % (backend, e))
    cache = backend_cls(location, params)
    # Some caches -- python-memcached in particular -- 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
    signals.request_finished.connect(cache.close)
    return cache
    return backend_cls(location, params)


class CacheHandler(object):
@@ -98,8 +98,10 @@ class CacheHandler(object):

    def __getitem__(self, alias):
        try:
            return getattr(self._caches, alias)
            return self._caches.caches[alias]
        except AttributeError:
            self._caches.caches = {}
        except KeyError:
            pass

        if alias not in settings.CACHES:
@@ -108,10 +110,12 @@ class CacheHandler(object):
            )

        cache = _create_cache(alias)
        setattr(self._caches, alias, cache)

        self._caches.caches[alias] = cache
        return cache

    def all(self):
        return getattr(self._caches, 'caches', {}).values()

caches = CacheHandler()


@@ -141,3 +145,11 @@ class DefaultCacheProxy(object):
        return caches[DEFAULT_CACHE_ALIAS] != other

cache = DefaultCacheProxy()

def close_caches(**kwargs):
    # Some caches -- python-memcached in particular -- 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():
        cache.close()
signals.request_finished.connect(close_caches)
+19 −1
Original line number Diff line number Diff line
@@ -203,7 +203,6 @@ _caches_setting_base = {
    'custom_key2': {'KEY_FUNCTION': 'cache.tests.custom_key_func'},
    'cull': {'OPTIONS': {'MAX_ENTRIES': 30}},
    'zero_cull': {'OPTIONS': {'CULL_FREQUENCY': 0, 'MAX_ENTRIES': 30}},
    'other': {'LOCATION': 'other'},
}


@@ -1014,6 +1013,13 @@ class LocMemCacheTests(BaseCacheTests, TestCase):
        caches['custom_key2']._cache = cache._cache
        caches['custom_key2']._expire_info = cache._expire_info

    @override_settings(CACHES={
        'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'},
        'other': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'other'
        },
    })
    def test_multiple_caches(self):
        "Check that multiple locmem caches are isolated"
        cache.set('value', 42)
@@ -1139,6 +1145,12 @@ class CustomCacheKeyValidationTests(TestCase):
        self.assertEqual(cache.get(key), val)


@override_settings(
    CACHES={
            'default': {
                'BACKEND': 'cache.closeable_cache.CacheClass',
            }
        },)
class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase):

    def test_simple(self):
@@ -1157,6 +1169,12 @@ class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase):
        self.assertRaises(InvalidCacheBackendError, get_cache, 'does_not_exist')

    def test_close(self):
        from django.core import signals
        self.assertFalse(cache.closed)
        signals.request_finished.send(self.__class__)
        self.assertTrue(cache.closed)

    def test_close_deprecated(self):
        from django.core.cache import get_cache
        from django.core import signals
        cache = get_cache('cache.closeable_cache.CacheClass')