Fixed #14596 -- Light refactoring of the cache backends.

* Removes some code duplication,
 * Provides a convenient base class for db-like cache backends
 * Adds tests for an edge case of culling,
 * Marks the memcached tests as "skipped", rather than omitting them.

Thanks to Jonas H for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14434 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-11-02 05:55:08 +00:00
parent ed51dd5d64
commit 1fc7c4aee4
5 changed files with 38 additions and 55 deletions

View File

@ -22,6 +22,18 @@ class BaseCache(object):
timeout = 300 timeout = 300
self.default_timeout = timeout self.default_timeout = timeout
max_entries = params.get('max_entries', 300)
try:
self._max_entries = int(max_entries)
except (ValueError, TypeError):
self._max_entries = 300
cull_frequency = params.get('cull_frequency', 3)
try:
self._cull_frequency = int(cull_frequency)
except (ValueError, TypeError):
self._cull_frequency = 3
def add(self, key, value, timeout=None): def add(self, key, value, timeout=None):
""" """
Set a value in the cache if the key does not already exist. If Set a value in the cache if the key does not already exist. If

View File

@ -25,7 +25,7 @@ class Options(object):
self.managed = True self.managed = True
self.proxy = False self.proxy = False
class CacheClass(BaseCache): class BaseDatabaseCacheClass(BaseCache):
def __init__(self, table, params): def __init__(self, table, params):
BaseCache.__init__(self, params) BaseCache.__init__(self, params)
self._table = table self._table = table
@ -34,17 +34,7 @@ class CacheClass(BaseCache):
_meta = Options(table) _meta = Options(table)
self.cache_model_class = CacheEntry self.cache_model_class = CacheEntry
max_entries = params.get('max_entries', 300) class CacheClass(BaseDatabaseCacheClass):
try:
self._max_entries = int(max_entries)
except (ValueError, TypeError):
self._max_entries = 300
cull_frequency = params.get('cull_frequency', 3)
try:
self._cull_frequency = int(cull_frequency)
except (ValueError, TypeError):
self._cull_frequency = 3
def get(self, key, default=None): def get(self, key, default=None):
self.validate_key(key) self.validate_key(key)
db = router.db_for_read(self.cache_model_class) db = router.db_for_read(self.cache_model_class)

View File

@ -14,19 +14,6 @@ from django.utils.hashcompat import md5_constructor
class CacheClass(BaseCache): class CacheClass(BaseCache):
def __init__(self, dir, params): def __init__(self, dir, params):
BaseCache.__init__(self, params) BaseCache.__init__(self, params)
max_entries = params.get('max_entries', 300)
try:
self._max_entries = int(max_entries)
except (ValueError, TypeError):
self._max_entries = 300
cull_frequency = params.get('cull_frequency', 3)
try:
self._cull_frequency = int(cull_frequency)
except (ValueError, TypeError):
self._cull_frequency = 3
self._dir = dir self._dir = dir
if not os.path.exists(self._dir): if not os.path.exists(self._dir):
self._createdir() self._createdir()

View File

@ -14,19 +14,6 @@ class CacheClass(BaseCache):
BaseCache.__init__(self, params) BaseCache.__init__(self, params)
self._cache = {} self._cache = {}
self._expire_info = {} self._expire_info = {}
max_entries = params.get('max_entries', 300)
try:
self._max_entries = int(max_entries)
except (ValueError, TypeError):
self._max_entries = 300
cull_frequency = params.get('cull_frequency', 3)
try:
self._cull_frequency = int(cull_frequency)
except (ValueError, TypeError):
self._cull_frequency = 3
self._lock = RWLock() self._lock = RWLock()
def add(self, key, value, timeout=None): def add(self, key, value, timeout=None):

View File

@ -410,6 +410,10 @@ class DBCacheTests(unittest.TestCase, BaseCacheTests):
def test_cull(self): def test_cull(self):
self.perform_cull_test(50, 29) self.perform_cull_test(50, 29)
def test_zero_cull(self):
self.cache = get_cache('db://%s?max_entries=30&cull_frequency=0' % self._table_name)
self.perform_cull_test(50, 18)
class LocMemCacheTests(unittest.TestCase, BaseCacheTests): class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
def setUp(self): def setUp(self):
self.cache = get_cache('locmem://?max_entries=30') self.cache = get_cache('locmem://?max_entries=30')
@ -417,30 +421,33 @@ class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
def test_cull(self): def test_cull(self):
self.perform_cull_test(50, 29) self.perform_cull_test(50, 29)
def test_zero_cull(self):
self.cache = get_cache('locmem://?max_entries=30&cull_frequency=0')
self.perform_cull_test(50, 19)
# memcached backend isn't guaranteed to be available. # memcached backend isn't guaranteed to be available.
# To check the memcached backend, the test settings file will # To check the memcached backend, the test settings file will
# need to contain a CACHE_BACKEND setting that points at # need to contain a CACHE_BACKEND setting that points at
# your memcache server. # your memcache server.
if settings.CACHE_BACKEND.startswith('memcached://'): class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
class MemcachedCacheTests(unittest.TestCase, BaseCacheTests): def setUp(self):
def setUp(self): self.cache = get_cache(settings.CACHE_BACKEND)
self.cache = get_cache(settings.CACHE_BACKEND)
def test_invalid_keys(self): def test_invalid_keys(self):
""" """
On memcached, we don't introduce a duplicate key validation On memcached, we don't introduce a duplicate key validation
step (for speed reasons), we just let the memcached API step (for speed reasons), we just let the memcached API
library raise its own exception on bad keys. Refs #6447. library raise its own exception on bad keys. Refs #6447.
In order to be memcached-API-library agnostic, we only assert In order to be memcached-API-library agnostic, we only assert
that a generic exception of some kind is raised. that a generic exception of some kind is raised.
"""
# memcached does not allow whitespace or control characters in keys
self.assertRaises(Exception, self.cache.set, 'key with spaces', 'value')
# memcached limits key length to 250
self.assertRaises(Exception, self.cache.set, 'a' * 251, 'value')
"""
# memcached does not allow whitespace or control characters in keys
self.assertRaises(Exception, self.cache.set, 'key with spaces', 'value')
# memcached limits key length to 250
self.assertRaises(Exception, self.cache.set, 'a' * 251, 'value')
MemcachedCacheTests = unittest.skipUnless(settings.CACHE_BACKEND.startswith('memcached://'), "memcached not available")(MemcachedCacheTests)
class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
""" """