Commits

Mike Bayer committed 4f4a5be Merge

merge refinements to redis backend

Comments (0)

Files changed (1)

dogpile/cache/backends/redis.py

 from __future__ import absolute_import
 from dogpile.cache.api import CacheBackend, NO_VALUE
 from dogpile.cache.compat import pickle
-import random
-import time
 
 redis = None
 
 __all__ = 'RedisBackend', 'RedisLock'
 
+
 class RedisBackend(CacheBackend):
     """A `Redis <http://redis.io/>`_ backend, using the
     `redis-py <http://pypi.python.org/pypi/redis/>`_ backend.
      When left at False, dogpile will coordinate on a regular
      threading mutex.
 
+    :param lock_timeout: integer, number of seconds after acquiring a lock that
+     Redis should expire it.
+
+    :param lock_sleep: integer, number of seconds to sleep when failed to
+     acquire a lock.
+
     """
 
     def __init__(self, arguments):
         self.port = arguments.pop('port', 6379)
         self.db = arguments.pop('db', 0)
         self.distributed_lock = arguments.get('distributed_lock', False)
+        self.lock_timeout = arguments.get('lock_timeout', None)
+        self.lock_sleep = arguments.get('lock_sleep', 0.1)
         self.redis_expiration_time = arguments.pop('redis_expiration_time', 0)
         self.client = self._create_client()
 
             return redis.StrictRedis.from_url(url=self.url)
         else:
             return redis.StrictRedis(host=self.host, password=self.password,
-                                        port=self.port, db=self.db)
+                                     port=self.port, db=self.db)
 
     def get_mutex(self, key):
         if self.distributed_lock:
-            return RedisLock(lambda: self.client, key)
+            return self.client.lock(u"_lock{}".format(key), self.lock_timeout,
+                                    self.lock_sleep)
         else:
             return None
 
         return pickle.loads(value)
 
     def get_multi(self, keys):
-        pipe = self.client.pipeline()
-        for key in keys:
-            pipe.get(key)
-        values = dict(zip(keys, pipe.execute()))
-        for key in keys:
-            if key in values and values[key] is not None:
-                values[key] = pickle.loads(values[key])
-            else:
-                values[key] = NO_VALUE
-        return values
+        values = self.client.mget(keys)
+        values = [pickle.loads(v) if v is not None else NO_VALUE
+                  for v in values]
+        return dict(zip(keys, values))
 
     def set(self, key, value):
         if self.redis_expiration_time:
             self.client.setex(key, self.redis_expiration_time,
-                    pickle.dumps(value))
+                              pickle.dumps(value))
         else:
             self.client.set(key, pickle.dumps(value))
 
     def set_multi(self, mapping):
-        pipe = self.client.pipeline()
-        for key,value in mapping.items():
-            if self.redis_expiration_time:
-                pipe.setex(key, self.redis_expiration_time,
-                        pickle.dumps(value))
-            else:
-                pipe.set(key, pickle.dumps(value))
-        pipe.execute()
+        mapping = dict((k, pickle.dumps(v)) for k, v in mapping.items())
+
+        if not self.redis_expiration_time:
+            self.client.mset(mapping)
+        else:
+            pipe = self.client.pipeline()
+            for key, value in mapping.items():
+                pipe.setex(key, self.redis_expiration_time, value)
+            pipe.execute()
 
     def delete(self, key):
         self.client.delete(key)
 
     def delete_multi(self, keys):
-        pipe = self.client.pipeline()
-        for key in keys:
-            pipe.delete(key)
-        pipe.execute()
-
-class RedisLock(object):
-    """Simple distributed lock using Redis.
-
-    This is an adaptation of the memcached lock featured at
-    http://amix.dk/blog/post/19386
-
-    """
-    def __init__(self, client_fn, key):
-        self.client_fn = client_fn
-        self.key = "_lock" + key
-
-    def acquire(self, wait=True):
-        client = self.client_fn()
-        i = 0
-        while True:
-            if client.setnx(self.key, 1):
-                return True
-            elif not wait:
-                return False
-            else:
-                sleep_time = (((i + 1) * random.random()) + 2 ** i) / 2.5
-                time.sleep(sleep_time)
-            if i < 15:
-                i += 1
-
-    def release(self):
-        client = self.client_fn()
-        client.delete(self.key)
-
-
+        self.client.delete(*keys)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.