Commit 638dbc3e authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #6464 -- Added incr() and decr() operations on cache backends. Atomic on...

Fixed #6464 -- Added incr() and decr() operations on cache backends. Atomic on Memcache; implemented as a 2 stage retrieve/update on other backends. Includes refactor of the cache tests to ensure all the backends are actually tested, and a fix to the DB cache backend that was discovered as a result. Thanks to Michael Malone for the original patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10031 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 1d8e6eae
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -65,6 +65,24 @@ class BaseCache(object):
        """
        return self.get(key) is not None

    def incr(self, key, delta=1):
        """
        Add delta to value in the cache. If the key does not exist, raise a
        ValueError exception.
        """
        if key not in self:
            raise ValueError, "Key '%s' not found" % key
        new_value = self.get(key) + delta
        self.set(key, new_value)
        return new_value

    def decr(self, key, delta=1):
        """
        Subtract delta from value in the cache. If the key does not exist, raise
        a ValueError exception.
        """
        return self.incr(key, -delta)

    def __contains__(self, key):
        """
        Returns True if the key is in the cache and has not expired.
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ class CacheClass(BaseCache):
                cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
        except DatabaseError:
            # To be threadsafe, updates/inserts are allowed to fail silently
            transaction.rollback()
            return False
        else:
            transaction.commit_unless_managed()
+5 −0
Original line number Diff line number Diff line
@@ -45,3 +45,8 @@ class CacheClass(BaseCache):
    def close(self, **kwargs):
        self._cache.disconnect_all()

    def incr(self, key, delta=1):
        return self._cache.incr(key, delta)

    def decr(self, key, delta=1):
        return self._cache.decr(key, delta)
+25 −18
Original line number Diff line number Diff line
@@ -782,6 +782,10 @@ dependencies:
    *  Textile_
    *  Docutils_
    *  setuptools_
    *  memcached_, plus the either the python-memcached_ or cmemcached_ Python binding

If you want to test the memcached cache backend, you will also need to define
a :setting:`CACHE_BACKEND` setting that points at your memcached instance.

Each of these dependencies is optional. If you're missing any of them, the
associated tests will be skipped.
@@ -791,6 +795,9 @@ associated tests will be skipped.
.. _Textile: http://pypi.python.org/pypi/textile
.. _docutils: http://pypi.python.org/pypi/docutils/0.4
.. _setuptools: http://pypi.python.org/pypi/setuptools/
.. _memcached: http://www.danga.com/memcached/
.. _python-memcached: http://pypi.python.org/pypi/python-memcached/
.. _cmemcached: http://pypi.python.org/pypi/cmemcache

To run a subset of the unit tests, append the names of the test modules to the
``runtests.py`` command line. See the list of directories in
+28 −1
Original line number Diff line number Diff line
@@ -439,6 +439,33 @@ of clearing the cache for a particular object::

    >>> cache.delete('a')

.. versionadded:: 1.1

You can also increment or decrement a key that already exists using the
``incr()`` or ``decr()`` methods, respectively. By default, the existing cache
value will incremented or decremented by 1. Other increment/decrement values
can be specified by providing an argument to the increment/decrement call. A
ValueError will be raised if you attempt to increment or decrement a
nonexistent cache key.::

    >>> cache.set('num', 1)
    >>> cache.incr('num')
    2
    >>> cache.incr('num', 10)
    12
    >>> cache.decr('num')
    11
    >>> cache.decr('num', 5)
    6

.. note::

    ``incr()``/``decr()`` methods are not guaranteed to be atomic. On those
    backends that support atomic increment/decrement (most notably, the
    memcached backend), increment and decrement operations will be atomic.
    However, if the backend doesn't natively provide an increment/decrement
    operation, it will be implemented using a 2 step retrieve/update.

That's it. The cache has very few restrictions: You can cache any object that
can be pickled safely, although keys must be strings.

Loading