Skip to content
This repository
Browse code

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...
commit 911cc952bd9ab56113de12ca9039cf50339e1f7f 1 parent b312117
Matt Robenolt authored March 27, 2012 sebleier committed July 06, 2012

Showing 1 changed file with 13 additions and 9 deletions. Show diff stats Hide diff stats

  1. 22  redis_cache/cache.py
22  redis_cache/cache.py
@@ -167,10 +167,7 @@ def add(self, key, value, timeout=None, version=None):
167 167
 
168 168
         Returns ``True`` if the object was added, ``False`` if not.
169 169
         """
170  
-        key = self.make_key(key, version=version)
171  
-        if self._client.exists(key):
172  
-            return False
173  
-        return self.set(key, value, timeout)
  170
+        return self.set(key, value, timeout, _add_only=True)
174 171
 
175 172
     def get(self, key, default=None, version=None):
176 173
         """
@@ -188,15 +185,22 @@ def get(self, key, default=None, version=None):
188 185
             result = self.unpickle(value)
189 186
         return result
190 187
 
191  
-    def _set(self, key, value, timeout, client):
  188
+    def _set(self, key, value, timeout, client, _add_only=False):
192 189
         if timeout == 0:
  190
+            if _add_only:
  191
+                return client.setnx(key, value)
193 192
             return client.set(key, value)
194 193
         elif timeout > 0:
195  
-            return client.setex(key, value, int(timeout))
  194
+            if _add_only:
  195
+                added = client.setnx(key, value)
  196
+                if added:
  197
+                    client.expire(key, timeout)
  198
+                return added
  199
+            return client.setex(key, value, timeout)
196 200
         else:
197 201
             return False
198 202
 
199  
-    def set(self, key, value, timeout=None, version=None, client=None):
  203
+    def set(self, key, value, timeout=None, version=None, client=None, _add_only=False):
200 204
         """
201 205
         Persist a value to the cache, and set an optional expiration time.
202 206
         """
@@ -211,9 +215,9 @@ def set(self, key, value, timeout=None, version=None, client=None):
211 215
             if int(value) != value:
212 216
                 raise TypeError
213 217
         except (ValueError, TypeError):
214  
-            result = self._set(key, pickle.dumps(value), int(timeout), client)
  218
+            result = self._set(key, pickle.dumps(value), int(timeout), client, _add_only)
215 219
         else:
216  
-            result = self._set(key, int(value), int(timeout), client)
  220
+            result = self._set(key, int(value), int(timeout), client, _add_only)
217 221
         # result is a boolean
218 222
         return result
219 223
 

0 notes on commit 911cc95

Please sign in to comment.
Something went wrong with that request. Please try again.