Source code for redisdb.backends

# -*- coding: utf-8 -*-

""" Backends which simplify access to redis through django cache framework. """

__doc__ = """
Backends
========
"""


from django.core.cache.backends.base import BaseCache
import hash_ring
import redis


[docs]class ConstRedis(redis.StrictRedis): """ Redis client that adds consistent hashing to :py:mod:`redis:StrictRedis`. """ def __init__(self, *args, **kwargs): self.hash_key = kwargs.pop('hash_key', 1) super(ConstRedis, self).__init__(*args, **kwargs) def __hash__(self): return self.hash_key def __str__(self): return '%s' % self.hash_key def close(self): # required by django interface but not used pass # pragma: no cover
class AbstractRedis(BaseCache): def __init__(self, location, params): self.db = params.pop('DB') options = params.pop('options', {}) super(AbstractRedis, self).__init__(params) self.create_nodes(location, **options) def create_nodes(self, location, **options): """ Create connections to redis nodes. @param str location: host:port indicating redis node location. @param dict options: redis options """ nodes = [] for i, node in enumerate(location): host, port = node.rsplit(':', 1) redis_pool = redis.ConnectionPool(host=host, port=port, db=self.db, **options) nodes.append(ConstRedis(connection_pool=redis_pool, hash_key=i, **options)) self.nodes = nodes self.nodes_ring = hash_ring.HashRing(nodes) WRITE_COMMANDS = [ 'append', 'bitop', 'blpop', 'brpop', 'brpoplpush', 'decr', 'decrby', 'del', 'expire', 'expireat', 'flushall', 'flushdb', 'getset', 'hdel', 'hincrby', 'hincrbyfloat', 'hmset', 'hset', 'hsetnx', 'incr', 'incrby', 'incrbyfloat', 'linsert', 'lpop', 'lpush', 'lpushx', 'lrem', 'lset', 'ltrim', 'mset', 'msetnx', 'persist', 'pexpire', 'pexpireat', 'pfadd', 'psetex', 'rename', 'renamenx', 'rpop', 'rpoplpush', 'rpush', 'rpushx', 'sadd', 'sdiffstore', 'set', 'setbit', 'setex', 'setnx', 'setrange', 'sinterstore', 'smove', 'sort', 'spop', 'srem', 'sunionstore', 'zadd', 'zincrby', 'zinterstore', 'zrem', 'zremrangebylex', 'zremrangebyrank', 'zremrangebyscore', 'zunionstore', ]
[docs]class RedisRing(AbstractRedis): """ RedisRing backend which writes only to one Redis server based on key. RedisRing is only a proxy to underlying :py:class:`ConstRedis` objects, which represents each Redis instance. It has the same methods set as :py:class:`ConstRedis`. Individual nodes can be accessed through `nodes` attribute. RedisRing is better suited for using Redis as memcached replacement. With one server failure data kept on that server have to be recreated. """ def __getattribute__(self, name): """ Proxy for StrictRedis attributes. """ if name in ['create_nodes', 'nodes', 'nodes_ring', 'db']: return object.__getattribute__(self, name) def wrapper(*args, **kwargs): try: key = args[0] except IndexError: key = '' node = self.nodes_ring.get_node(key) func = getattr(node, name) return func(*args, **kwargs) return wrapper
[docs]class RedisCopy(AbstractRedis): """ Redis backend which writes to all Redis servers. RedisCopy is only a proxy to underlying :py:class:`ConstRedis` objects, which represents each Redis instance. It has the same methods set as :py:class:`ConstRedis`. Individual nodes can be accessed through `nodes` attribute. Fetching is done only from one server based on key just like in RedisRing. RedisCopy can be seen like master/master configuration where synchronization isn't done by Redis itself but by clients. It's well suited for storing data that should be available even if one server goes down. """ def __getattribute__(self, name): """ Proxy for StrictRedis attributes. """ if name in ['create_nodes', 'nodes', 'nodes_ring', 'db']: return object.__getattribute__(self, name) def wrapper(*args, **kwargs): try: key = args[0] except IndexError: key = '' if name not in WRITE_COMMANDS: node = self.nodes_ring.get_node(key) func = getattr(node, name) return func(*args, **kwargs) else: results = [] for node in self.nodes: func = getattr(node, name) results.append(func(*args, **kwargs)) return results return wrapper
__all__ = ('RedisCopy', 'RedisRing')