Ticket #12671: cache.diff

File cache.diff, 10.2 KB (added by Jeff Balogh, 14 years ago)

Adds set_many, delete_many, and clear methods to cache

  • django/core/cache/backends/base.py

    diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py
    index d6f07ef..7429bb0 100644
    a b class BaseCache(object):  
    9191        # so that it always has the same functionality as has_key(), even
    9292        # if a subclass overrides it.
    9393        return self.has_key(key)
     94
     95    def set_many(self, mapping, timeout=None):
     96        """
     97        Set a bunch of values in the cache at once from a dict of key/value
     98        pairs.  For certain backends (memcached), this is much more efficient
     99        than calling set() multiple times.
     100
     101        If timeout is given, that timeout will be used for the key; otherwise
     102        the default cache timeout will be used.
     103        """
     104        for key, value in mapping.items():
     105            self.set(key, value, timeout)
     106
     107    def delete_many(self, keys):
     108        """
     109        Set a bunch of values in the cache at once.  For certain backends
     110        (memcached), this is much more efficient than calling delete() multiple
     111        times.
     112        """
     113        for key in keys:
     114            self.delete(key)
     115
     116    def clear(self):
     117        """Remove *all* values from the cache at once."""
     118        raise NotImplementedError
  • django/core/cache/backends/db.py

    diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py
    index eb459a0..13dc5c1 100644
    a b class CacheClass(BaseCache):  
    8484
    8585    def _cull(self, cursor, now):
    8686        if self._cull_frequency == 0:
    87             cursor.execute("DELETE FROM %s" % self._table)
     87            self.clear()
    8888        else:
    8989            cursor.execute("DELETE FROM %s WHERE expires < %%s" % self._table, [str(now)])
    9090            cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
    class CacheClass(BaseCache):  
    9292            if num > self._max_entries:
    9393                cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % self._table, [num / self._cull_frequency])
    9494                cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % self._table, [cursor.fetchone()[0]])
     95
     96    def clear(self):
     97        cursor = connection.cursor()
     98        cursor.execute('DELETE FROM %s' % self._table)
  • django/core/cache/backends/dummy.py

    diff --git a/django/core/cache/backends/dummy.py b/django/core/cache/backends/dummy.py
    index e479703..4337484 100644
    a b class CacheClass(BaseCache):  
    2323
    2424    def has_key(self, *args, **kwargs):
    2525        return False
     26
     27    def set_many(self, *args, **kwargs):
     28        pass
     29
     30    def delete_many(self, *args, **kwargs):
     31        pass
     32
     33    def clear(self):
     34        pass
  • django/core/cache/backends/filebased.py

    diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py
    index 87baf8b..91f0b78 100644
    a b  
    22
    33import os
    44import time
     5import shutil
    56try:
    67    import cPickle as pickle
    78except ImportError:
    class CacheClass(BaseCache):  
    150151            count += len(files)
    151152        return count
    152153    _num_entries = property(_get_num_entries)
     154
     155    def clear(self):
     156        try:
     157            shutil.rmtree(self._dir)
     158        except (IOError, OSError):
     159            pass
  • django/core/cache/backends/locmem.py

    diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py
    index 91e2f2f..5df891f 100644
    a b class CacheClass(BaseCache):  
    110110
    111111    def _cull(self):
    112112        if self._cull_frequency == 0:
    113             self._cache.clear()
    114             self._expire_info.clear()
     113            self.clear()
    115114        else:
    116115            doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0]
    117116            for k in doomed:
    class CacheClass(BaseCache):  
    133132            self._delete(key)
    134133        finally:
    135134            self._lock.writer_leaves()
     135
     136    def clear(self):
     137        self._cache.clear()
     138        self._expire_info.clear()
  • django/core/cache/backends/memcached.py

    diff --git a/django/core/cache/backends/memcached.py b/django/core/cache/backends/memcached.py
    index d5e6394..65932ad 100644
    a b class CacheClass(BaseCache):  
    7171        if val is None:
    7272            raise ValueError("Key '%s' not found" % key)
    7373        return val
     74
     75    def set_many(self, mapping, timeout=0):
     76        safe_mapping = {}
     77        for key, value in mapping.items():
     78            if isinstance(value, unicode):
     79                value = value.encode('utf-8')
     80            safe_mapping[smart_str(key)] = value
     81        self._cache.set_multi(safe_mapping, timeout or self.default_timeout)
     82
     83    def delete_many(self, keys):
     84        self._cache.delete_multi(map(smart_str, keys))
     85
     86    def clear(self):
     87        self._cache.flush_all()
  • docs/topics/cache.txt

    diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
    index f7b1fd5..8438876 100644
    a b It's used by sites such as Facebook and Wikipedia to reduce database access and  
    6262dramatically increase site performance.
    6363
    6464Memcached is available for free at http://danga.com/memcached/ . It runs as a
    65 daemon and is allotted a specified amount of RAM. All it does is provide an
     65daemon and is allotted a specified amount of RAM. All it does is provide a
    6666fast interface for adding, retrieving and deleting arbitrary data in the cache.
    6767All data is stored directly in memory, so there's no overhead of database or
    6868filesystem usage.
    actually exist in the cache (and haven't expired)::  
    522522    >>> cache.get_many(['a', 'b', 'c'])
    523523    {'a': 1, 'b': 2, 'c': 3}
    524524
    525 Finally, you can delete keys explicitly with ``delete()``. This is an easy way
    526 of clearing the cache for a particular object::
     525.. versionadded:: 1.2
     526
     527To set multiple values more efficiently, use ``set_many()`` to pass a dictionary
     528of key-value pairs::
     529
     530    >>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
     531    >>> cache.get_many(['a', 'b', 'c'])
     532    {'a': 1, 'b': 2, 'c': 3}
     533
     534Like ``cache.set()``, ``set_many()`` takes an optional ``timeout`` parameter.
     535
     536You can delete keys explicitly with ``delete()``. This is an easy way of
     537clearing the cache for a particular object::
    527538
    528539    >>> cache.delete('a')
    529540
     541.. versionadded:: 1.2
     542
     543If you want to clear a bunch of keys at once, ``delete_many()`` can take a list
     544of keys to be cleared::
     545
     546    >>> cache.delete_many(['a', 'b', 'c'])
     547
     548.. versionadded:: 1.2
     549
     550Finally, if you want to delete all the keys in the cache, use
     551``cache.clear()``.  Be careful with this; ``clear()`` will remove *everything*
     552from the cache, not just the keys set by your application. ::
     553
     554    >>> cache.clear()
     555
    530556.. versionadded:: 1.1
    531557
    532558You can also increment or decrement a key that already exists using the
  • tests/regressiontests/cache/tests.py

    diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
    index 593b726..86d8ca2 100644
    a b class DummyCacheTests(unittest.TestCase):  
    129129            self.cache.set(key, value)
    130130            self.assertEqual(self.cache.get(key), None)
    131131
     132    def test_set_many(self):
     133        "set_many does nothing for the dummy cache backend"
     134        self.cache.set_many({'a': 1, 'b': 2})
     135
     136    def test_delete_many(self):
     137        "delete_many does nothing for the dummy cache backend"
     138        self.cache.delete_many(['a', 'b'])
     139
     140    def test_clear(self):
     141        "clear does nothing for the dummy cache backend"
     142        self.cache.clear()
     143
    132144
    133145class BaseCacheTests(object):
    134146    # A common set of tests to apply to all cache backends
     147    def tearDown(self):
     148        self.cache.clear()
     149
    135150    def test_simple(self):
    136151        # Simple cache set/get works
    137152        self.cache.set("key", "value")
    class BaseCacheTests(object):  
    278293            self.cache.set(key, value)
    279294            self.assertEqual(self.cache.get(key), value)
    280295
     296    def test_set_many(self):
     297        # Multiple keys can be set using set_many
     298        self.cache.set_many({"key1": "spam", "key2": "eggs"})
     299        self.assertEqual(self.cache.get("key1"), "spam")
     300        self.assertEqual(self.cache.get("key2"), "eggs")
     301
     302    def test_set_many_expiration(self):
     303        # set_many takes a second ``timeout`` parameter
     304        self.cache.set_many({"key1": "spam", "key2": "eggs"}, 1)
     305        time.sleep(2)
     306        self.assertEqual(self.cache.get("key1"), None)
     307        self.assertEqual(self.cache.get("key2"), None)
     308
     309    def test_delete_many(self):
     310        # Multiple keys can be deleted using delete_many
     311        self.cache.set("key1", "spam")
     312        self.cache.set("key2", "eggs")
     313        self.cache.set("key3", "ham")
     314        self.cache.delete_many(["key1", "key2"])
     315        self.assertEqual(self.cache.get("key1"), None)
     316        self.assertEqual(self.cache.get("key2"), None)
     317        self.assertEqual(self.cache.get("key3"), "ham")
     318
     319    def test_clear(self):
     320        # The cache can be emptied using clear
     321        self.cache.set("key1", "spam")
     322        self.cache.set("key2", "eggs")
     323        self.cache.clear()
     324        self.assertEqual(self.cache.get("key1"), None)
     325        self.assertEqual(self.cache.get("key2"), None)
     326
    281327class DBCacheTests(unittest.TestCase, BaseCacheTests):
    282328    def setUp(self):
    283329        management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False)
    class DBCacheTests(unittest.TestCase, BaseCacheTests):  
    286332    def tearDown(self):
    287333        from django.db import connection
    288334        cursor = connection.cursor()
    289         cursor.execute('DROP TABLE test_cache_table');
     335        cursor.execute('DROP TABLE test_cache_table')
    290336
    291337class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
    292338    def setUp(self):
    class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):  
    309355        self.dirname = tempfile.mkdtemp()
    310356        self.cache = get_cache('file://%s' % self.dirname)
    311357
    312     def tearDown(self):
    313         shutil.rmtree(self.dirname)
    314 
    315358    def test_hashing(self):
    316359        """Test that keys are hashed into subdirectories correctly"""
    317360        self.cache.set("foo", "bar")
Back to Top