Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #12671 -- Added set_many(), get_many(), and clear() methods to …

…the cache backend interface. Thanks to Jeff Balogh for the report and patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12306 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 8e8d4b5888b73e5c0b2cfc77be4c6d5898546654 1 parent c6ee1f6
@freakboy3742 freakboy3742 authored
View
1  AUTHORS
@@ -56,6 +56,7 @@ answer newbie questions, and generally made Django that much better:
Mike Axiak <axiak@mit.edu>
Niran Babalola <niran@niran.org>
Morten Bagai <m@bagai.com>
+ Jeff Balogh <jbalogh@mozilla.com>
Mikaël Barbero <mikael.barbero nospam at nospam free.fr>
Randy Barlow <randy@electronsweatshop.com>
Scott Barr <scott@divisionbyzero.com.au>
View
25 django/core/cache/backends/base.py
@@ -91,3 +91,28 @@ def __contains__(self, key):
# so that it always has the same functionality as has_key(), even
# if a subclass overrides it.
return self.has_key(key)
+
+ def set_many(self, data, timeout=None):
+ """
+ Set a bunch of values in the cache at once from a dict of key/value
+ pairs. For certain backends (memcached), this is much more efficient
+ than calling set() multiple times.
+
+ If timeout is given, that timeout will be used for the key; otherwise
+ the default cache timeout will be used.
+ """
+ for key, value in data.items():
+ self.set(key, value, timeout)
+
+ def delete_many(self, keys):
+ """
+ Set a bunch of values in the cache at once. For certain backends
+ (memcached), this is much more efficient than calling delete() multiple
+ times.
+ """
+ for key in keys:
+ self.delete(key)
+
+ def clear(self):
+ """Remove *all* values from the cache at once."""
+ raise NotImplementedError
View
6 django/core/cache/backends/db.py
@@ -84,7 +84,7 @@ def has_key(self, key):
def _cull(self, cursor, now):
if self._cull_frequency == 0:
- cursor.execute("DELETE FROM %s" % self._table)
+ self.clear()
else:
cursor.execute("DELETE FROM %s WHERE expires < %%s" % self._table, [str(now)])
cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
@@ -92,3 +92,7 @@ def _cull(self, cursor, now):
if num > self._max_entries:
cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % self._table, [num / self._cull_frequency])
cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % self._table, [cursor.fetchone()[0]])
+
+ def clear(self):
+ cursor = connection.cursor()
+ cursor.execute('DELETE FROM %s' % self._table)
View
9 django/core/cache/backends/dummy.py
@@ -23,3 +23,12 @@ def get_many(self, *args, **kwargs):
def has_key(self, *args, **kwargs):
return False
+
+ def set_many(self, *args, **kwargs):
+ pass
+
+ def delete_many(self, *args, **kwargs):
+ pass
+
+ def clear(self):
+ pass
View
7 django/core/cache/backends/filebased.py
@@ -2,6 +2,7 @@
import os
import time
+import shutil
try:
import cPickle as pickle
except ImportError:
@@ -150,3 +151,9 @@ def _get_num_entries(self):
count += len(files)
return count
_num_entries = property(_get_num_entries)
+
+ def clear(self):
+ try:
+ shutil.rmtree(self._dir)
+ except (IOError, OSError):
+ pass
View
7 django/core/cache/backends/locmem.py
@@ -110,8 +110,7 @@ def has_key(self, key):
def _cull(self):
if self._cull_frequency == 0:
- self._cache.clear()
- self._expire_info.clear()
+ self.clear()
else:
doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0]
for k in doomed:
@@ -133,3 +132,7 @@ def delete(self, key):
self._delete(key)
finally:
self._lock.writer_leaves()
+
+ def clear(self):
+ self._cache.clear()
+ self._expire_info.clear()
View
14 django/core/cache/backends/memcached.py
@@ -71,3 +71,17 @@ def decr(self, key, delta=1):
if val is None:
raise ValueError("Key '%s' not found" % key)
return val
+
+ def set_many(self, data, timeout=0):
+ safe_data = {}
+ for key, value in data.items():
+ if isinstance(value, unicode):
+ value = value.encode('utf-8')
+ safe_data[smart_str(key)] = value
+ self._cache.set_multi(safe_data, timeout or self.default_timeout)
+
+ def delete_many(self, keys):
+ self._cache.delete_multi(map(smart_str, keys))
+
+ def clear(self):
+ self._cache.flush_all()
View
32 docs/topics/cache.txt
@@ -62,7 +62,7 @@ It's used by sites such as Facebook and Wikipedia to reduce database access and
dramatically increase site performance.
Memcached is available for free at http://danga.com/memcached/ . It runs as a
-daemon and is allotted a specified amount of RAM. All it does is provide an
+daemon and is allotted a specified amount of RAM. All it does is provide a
fast interface for adding, retrieving and deleting arbitrary data in the cache.
All data is stored directly in memory, so there's no overhead of database or
filesystem usage.
@@ -522,11 +522,37 @@ actually exist in the cache (and haven't expired)::
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
-Finally, you can delete keys explicitly with ``delete()``. This is an easy way
-of clearing the cache for a particular object::
+.. versionadded:: 1.2
+
+To set multiple values more efficiently, use ``set_many()`` to pass a dictionary
+of key-value pairs::
+
+ >>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
+ >>> cache.get_many(['a', 'b', 'c'])
+ {'a': 1, 'b': 2, 'c': 3}
+
+Like ``cache.set()``, ``set_many()`` takes an optional ``timeout`` parameter.
+
+You can delete keys explicitly with ``delete()``. This is an easy way of
+clearing the cache for a particular object::
>>> cache.delete('a')
+.. versionadded:: 1.2
+
+If you want to clear a bunch of keys at once, ``delete_many()`` can take a list
+of keys to be cleared::
+
+ >>> cache.delete_many(['a', 'b', 'c'])
+
+.. versionadded:: 1.2
+
+Finally, if you want to delete all the keys in the cache, use
+``cache.clear()``. Be careful with this; ``clear()`` will remove *everything*
+from the cache, not just the keys set by your application. ::
+
+ >>> cache.clear()
+
.. versionadded:: 1.1
You can also increment or decrement a key that already exists using the
View
51 tests/regressiontests/cache/tests.py
@@ -129,9 +129,24 @@ def test_unicode(self):
self.cache.set(key, value)
self.assertEqual(self.cache.get(key), None)
+ def test_set_many(self):
+ "set_many does nothing for the dummy cache backend"
+ self.cache.set_many({'a': 1, 'b': 2})
+
+ def test_delete_many(self):
+ "delete_many does nothing for the dummy cache backend"
+ self.cache.delete_many(['a', 'b'])
+
+ def test_clear(self):
+ "clear does nothing for the dummy cache backend"
+ self.cache.clear()
+
class BaseCacheTests(object):
# A common set of tests to apply to all cache backends
+ def tearDown(self):
+ self.cache.clear()
+
def test_simple(self):
# Simple cache set/get works
self.cache.set("key", "value")
@@ -278,6 +293,37 @@ def test_unicode(self):
self.cache.set(key, value)
self.assertEqual(self.cache.get(key), value)
+ def test_set_many(self):
+ # Multiple keys can be set using set_many
+ self.cache.set_many({"key1": "spam", "key2": "eggs"})
+ self.assertEqual(self.cache.get("key1"), "spam")
+ self.assertEqual(self.cache.get("key2"), "eggs")
+
+ def test_set_many_expiration(self):
+ # set_many takes a second ``timeout`` parameter
+ self.cache.set_many({"key1": "spam", "key2": "eggs"}, 1)
+ time.sleep(2)
+ self.assertEqual(self.cache.get("key1"), None)
+ self.assertEqual(self.cache.get("key2"), None)
+
+ def test_delete_many(self):
+ # Multiple keys can be deleted using delete_many
+ self.cache.set("key1", "spam")
+ self.cache.set("key2", "eggs")
+ self.cache.set("key3", "ham")
+ self.cache.delete_many(["key1", "key2"])
+ self.assertEqual(self.cache.get("key1"), None)
+ self.assertEqual(self.cache.get("key2"), None)
+ self.assertEqual(self.cache.get("key3"), "ham")
+
+ def test_clear(self):
+ # The cache can be emptied using clear
+ self.cache.set("key1", "spam")
+ self.cache.set("key2", "eggs")
+ self.cache.clear()
+ self.assertEqual(self.cache.get("key1"), None)
+ self.assertEqual(self.cache.get("key2"), None)
+
class DBCacheTests(unittest.TestCase, BaseCacheTests):
def setUp(self):
management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False)
@@ -286,7 +332,7 @@ def setUp(self):
def tearDown(self):
from django.db import connection
cursor = connection.cursor()
- cursor.execute('DROP TABLE test_cache_table');
+ cursor.execute('DROP TABLE test_cache_table')
class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
def setUp(self):
@@ -309,9 +355,6 @@ def setUp(self):
self.dirname = tempfile.mkdtemp()
self.cache = get_cache('file://%s' % self.dirname)
- def tearDown(self):
- shutil.rmtree(self.dirname)
-
def test_hashing(self):
"""Test that keys are hashed into subdirectories correctly"""
self.cache.set("foo", "bar")
Please sign in to comment.
Something went wrong with that request. Please try again.