OLD | NEW |
(Empty) | |
| 1 """ |
| 2 Redis Backends |
| 3 ------------------ |
| 4 |
| 5 Provides backends for talking to `Redis <http://redis.io>`_. |
| 6 |
| 7 """ |
| 8 |
| 9 from __future__ import absolute_import |
| 10 from ..api import CacheBackend, NO_VALUE |
| 11 from ...util.compat import pickle, u |
| 12 |
| 13 redis = None |
| 14 |
| 15 __all__ = 'RedisBackend', |
| 16 |
| 17 |
| 18 class RedisBackend(CacheBackend): |
| 19 """A `Redis <http://redis.io/>`_ backend, using the |
| 20 `redis-py <http://pypi.python.org/pypi/redis/>`_ backend. |
| 21 |
| 22 Example configuration:: |
| 23 |
| 24 from dogpile.cache import make_region |
| 25 |
| 26 region = make_region().configure( |
| 27 'dogpile.cache.redis', |
| 28 arguments = { |
| 29 'host': 'localhost', |
| 30 'port': 6379, |
| 31 'db': 0, |
| 32 'redis_expiration_time': 60*60*2, # 2 hours |
| 33 'distributed_lock': True |
| 34 } |
| 35 ) |
| 36 |
| 37 Arguments accepted in the arguments dictionary: |
| 38 |
| 39 :param url: string. If provided, will override separate host/port/db |
| 40 params. The format is that accepted by ``StrictRedis.from_url()``. |
| 41 |
| 42 .. versionadded:: 0.4.1 |
| 43 |
| 44 :param host: string, default is ``localhost``. |
| 45 |
| 46 :param password: string, default is no password. |
| 47 |
| 48 .. versionadded:: 0.4.1 |
| 49 |
| 50 :param port: integer, default is ``6379``. |
| 51 |
| 52 :param db: integer, default is ``0``. |
| 53 |
| 54 :param redis_expiration_time: integer, number of seconds after setting |
| 55 a value that Redis should expire it. This should be larger than dogpile's |
| 56 cache expiration. By default no expiration is set. |
| 57 |
| 58 :param distributed_lock: boolean, when True, will use a |
| 59 redis-lock as the dogpile lock. |
| 60 Use this when multiple |
| 61 processes will be talking to the same redis instance. |
| 62 When left at False, dogpile will coordinate on a regular |
| 63 threading mutex. |
| 64 |
| 65 :param lock_timeout: integer, number of seconds after acquiring a lock that |
| 66 Redis should expire it. This argument is only valid when |
| 67 ``distributed_lock`` is ``True``. |
| 68 |
| 69 .. versionadded:: 0.5.0 |
| 70 |
| 71 :param socket_timeout: float, seconds for socket timeout. |
| 72 Default is None (no timeout). |
| 73 |
| 74 .. versionadded:: 0.5.4 |
| 75 |
| 76 :param lock_sleep: integer, number of seconds to sleep when failed to |
| 77 acquire a lock. This argument is only valid when |
| 78 ``distributed_lock`` is ``True``. |
| 79 |
| 80 .. versionadded:: 0.5.0 |
| 81 |
| 82 :param connection_pool: ``redis.ConnectionPool`` object. If provided, |
| 83 this object supersedes other connection arguments passed to the |
| 84 ``redis.StrictRedis`` instance, including url and/or host as well as |
| 85 socket_timeout, and will be passed to ``redis.StrictRedis`` as the |
| 86 source of connectivity. |
| 87 |
| 88 .. versionadded:: 0.5.4 |
| 89 |
| 90 |
| 91 """ |
| 92 |
| 93 def __init__(self, arguments): |
| 94 arguments = arguments.copy() |
| 95 self._imports() |
| 96 self.url = arguments.pop('url', None) |
| 97 self.host = arguments.pop('host', 'localhost') |
| 98 self.password = arguments.pop('password', None) |
| 99 self.port = arguments.pop('port', 6379) |
| 100 self.db = arguments.pop('db', 0) |
| 101 self.distributed_lock = arguments.get('distributed_lock', False) |
| 102 self.socket_timeout = arguments.pop('socket_timeout', None) |
| 103 |
| 104 self.lock_timeout = arguments.get('lock_timeout', None) |
| 105 self.lock_sleep = arguments.get('lock_sleep', 0.1) |
| 106 |
| 107 self.redis_expiration_time = arguments.pop('redis_expiration_time', 0) |
| 108 self.connection_pool = arguments.get('connection_pool', None) |
| 109 self.client = self._create_client() |
| 110 |
| 111 def _imports(self): |
| 112 # defer imports until backend is used |
| 113 global redis |
| 114 import redis # noqa |
| 115 |
| 116 def _create_client(self): |
| 117 if self.connection_pool is not None: |
| 118 # the connection pool already has all other connection |
| 119 # options present within, so here we disregard socket_timeout |
| 120 # and others. |
| 121 return redis.StrictRedis(connection_pool=self.connection_pool) |
| 122 |
| 123 args = {} |
| 124 if self.socket_timeout: |
| 125 args['socket_timeout'] = self.socket_timeout |
| 126 |
| 127 if self.url is not None: |
| 128 args.update(url=self.url) |
| 129 return redis.StrictRedis.from_url(**args) |
| 130 else: |
| 131 args.update( |
| 132 host=self.host, password=self.password, |
| 133 port=self.port, db=self.db |
| 134 ) |
| 135 return redis.StrictRedis(**args) |
| 136 |
| 137 def get_mutex(self, key): |
| 138 if self.distributed_lock: |
| 139 return self.client.lock(u('_lock{0}').format(key), |
| 140 self.lock_timeout, self.lock_sleep) |
| 141 else: |
| 142 return None |
| 143 |
| 144 def get(self, key): |
| 145 value = self.client.get(key) |
| 146 if value is None: |
| 147 return NO_VALUE |
| 148 return pickle.loads(value) |
| 149 |
| 150 def get_multi(self, keys): |
| 151 if not keys: |
| 152 return [] |
| 153 values = self.client.mget(keys) |
| 154 return [ |
| 155 pickle.loads(v) if v is not None else NO_VALUE |
| 156 for v in values] |
| 157 |
| 158 def set(self, key, value): |
| 159 if self.redis_expiration_time: |
| 160 self.client.setex(key, self.redis_expiration_time, |
| 161 pickle.dumps(value, pickle.HIGHEST_PROTOCOL)) |
| 162 else: |
| 163 self.client.set(key, pickle.dumps(value, pickle.HIGHEST_PROTOCOL)) |
| 164 |
| 165 def set_multi(self, mapping): |
| 166 mapping = dict( |
| 167 (k, pickle.dumps(v, pickle.HIGHEST_PROTOCOL)) |
| 168 for k, v in mapping.items() |
| 169 ) |
| 170 |
| 171 if not self.redis_expiration_time: |
| 172 self.client.mset(mapping) |
| 173 else: |
| 174 pipe = self.client.pipeline() |
| 175 for key, value in mapping.items(): |
| 176 pipe.setex(key, self.redis_expiration_time, value) |
| 177 pipe.execute() |
| 178 |
| 179 def delete(self, key): |
| 180 self.client.delete(key) |
| 181 |
| 182 def delete_multi(self, keys): |
| 183 self.client.delete(*keys) |
OLD | NEW |