Permalink
Browse files

Implement cache.add with SETNX so cache.add can be atomic like it's e…

…xpected

The way it is now, doing a cache.add has a slight race condition
between checking if the key exists already before setting.

This rewrite uses the native SETNX command to only set the key if one
doesn't exist yet.
  • Loading branch information...
1 parent b312117 commit 911cc952bd9ab56113de12ca9039cf50339e1f7f @mattrobenolt mattrobenolt committed with Mar 28, 2012
Showing with 13 additions and 9 deletions.
  1. +13 −9 redis_cache/cache.py
View
@@ -167,10 +167,7 @@ def add(self, key, value, timeout=None, version=None):
Returns ``True`` if the object was added, ``False`` if not.
"""
- key = self.make_key(key, version=version)
- if self._client.exists(key):
- return False
- return self.set(key, value, timeout)
+ return self.set(key, value, timeout, _add_only=True)
def get(self, key, default=None, version=None):
"""
@@ -188,15 +185,22 @@ def get(self, key, default=None, version=None):
result = self.unpickle(value)
return result
- def _set(self, key, value, timeout, client):
+ def _set(self, key, value, timeout, client, _add_only=False):
if timeout == 0:
+ if _add_only:
+ return client.setnx(key, value)
return client.set(key, value)
elif timeout > 0:
- return client.setex(key, value, int(timeout))
+ if _add_only:
+ added = client.setnx(key, value)
+ if added:
+ client.expire(key, timeout)
+ return added
+ return client.setex(key, value, timeout)
else:
return False
- def set(self, key, value, timeout=None, version=None, client=None):
+ def set(self, key, value, timeout=None, version=None, client=None, _add_only=False):
"""
Persist a value to the cache, and set an optional expiration time.
"""
@@ -211,9 +215,9 @@ def set(self, key, value, timeout=None, version=None, client=None):
if int(value) != value:
raise TypeError
except (ValueError, TypeError):
- result = self._set(key, pickle.dumps(value), int(timeout), client)
+ result = self._set(key, pickle.dumps(value), int(timeout), client, _add_only)
else:
- result = self._set(key, int(value), int(timeout), client)
+ result = self._set(key, int(value), int(timeout), client, _add_only)
# result is a boolean
return result

0 comments on commit 911cc95

Please sign in to comment.