Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,34 @@ development_settings.py
.settings
dist/*
*.rdb
dist/*
MANIFEST
.venv
redis/
*/_build/
build/*
/.idea/*

### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# CMake
cmake-build-*/

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
13 changes: 13 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,19 @@ Requirements
},
}

# For Cluster Setup
# redis-server instance.
CACHES = {
'default': {
'BACKEND': 'redis_cache.RedisClusterCache',
'LOCATION': [
'<host>:<port>',
'<host>:<port>',
'<host>:<port>',
]
},
}



Usage
Expand Down
1 change: 1 addition & 0 deletions redis_cache/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from redis_cache.backends.single import RedisCache
from redis_cache.backends.multiple import ShardedRedisCache
from redis_cache.backends.dummy import RedisDummyCache
from redis_cache.backends.cluster import RedisClusterCache
117 changes: 117 additions & 0 deletions redis_cache/backends/cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
try:
import cPickle as pickle
except ImportError:
import pickle
import random

from rediscluster import RedisCluster

from redis_cache.compat import DEFAULT_TIMEOUT
from redis_cache.utils import parse_connection_kwargs

from redis_cache.backends.base import BaseRedisCache


class RedisClusterCache(BaseRedisCache):

def __init__(self, server, params):
"""
Connect to Redis, and set up cache backend.
"""
super(RedisClusterCache, self).__init__(server, params)

conn_params = {
'startup_nodes': [],
'decode_responses': True
}

for server in self.servers:
server_params = parse_connection_kwargs(server)
conn_params['startup_nodes'].append(server_params)

client = RedisCluster(**conn_params)
self.clients['cluster'] = client

self.client_list = self.clients.values()
self.master_client = self.get_master_client()

def get_client(self, key, write=False):
if write and self.master_client is not None:
return self.master_client
return random.choice(list(self.client_list))

####################
# Django cache api #
####################

def delete_many(self, keys, version=None):
"""Remove multiple keys at once."""
versioned_keys = self.make_keys(keys, version=version)
if versioned_keys:
self._delete_many(self.master_client, versioned_keys)

def clear(self, version=None):
"""Flush cache keys.

If version is specified, all keys belonging the version's key
namespace will be deleted. Otherwise, all keys will be deleted.
"""
if version is None:
self._clear(self.master_client)
else:
self.delete_pattern('*', version=version)

def get_many(self, keys, version=None):
versioned_keys = self.make_keys(keys, version=version)
return self._get_many(self.master_client, keys, versioned_keys=versioned_keys)

def set_many(self, data, timeout=DEFAULT_TIMEOUT, version=None):
"""
Set a bunch of values in the cache at once from a dict of key/value
pairs. This is much more efficient than calling set() multiple times.

If timeout is given, that timeout will be used for the key; otherwise
the default cache timeout will be used.
"""
timeout = self.get_timeout(timeout)

versioned_keys = self.make_keys(data.keys(), version=version)
if timeout is None:
new_data = {}
for key in versioned_keys:
new_data[key] = self.prep_value(data[key._original_key])
return self._set_many(self.master_client, new_data)

pipeline = self.master_client.pipeline()
for key in versioned_keys:
value = self.prep_value(data[key._original_key])
self._set(pipeline, key, value, timeout)
pipeline.execute()

def incr_version(self, key, delta=1, version=None):
"""
Adds delta to the cache version for the supplied key. Returns the
new version.

"""
if version is None:
version = self.version

old = self.make_key(key, version)
new = self.make_key(key, version=version + delta)

return self._incr_version(self.master_client, old, new, delta, version)

#####################
# Extra api methods #
#####################

def delete_pattern(self, pattern, version=None):
pattern = self.make_key(pattern, version=version)
self._delete_pattern(self.master_client, pattern)

def reinsert_keys(self):
"""
Reinsert cache entries using the current pickle protocol version.
"""
self._reinsert_keys(self.master_client)
44 changes: 44 additions & 0 deletions redis_cache/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import sys
import django


PY3 = (sys.version_info >= (3,))

try:
# Django 1.5+
from django.utils.encoding import smart_text, smart_bytes
except ImportError:
# older Django, thus definitely Python 2
from django.utils.encoding import smart_unicode, smart_str
smart_text = smart_unicode
smart_bytes = smart_str

if PY3:
bytes_type = bytes
from urllib.parse import parse_qs, urlparse
else:
bytes_type = str
from urlparse import parse_qs, urlparse


if django.VERSION[:2] >= (1, 6):
from django.core.cache.backends.base import DEFAULT_TIMEOUT as DJANGO_DEFAULT_TIMEOUT
DEFAULT_TIMEOUT = DJANGO_DEFAULT_TIMEOUT
else:
DEFAULT_TIMEOUT = None


def python_2_unicode_compatible(klass):
"""
A decorator that defines __unicode__ and __str__ methods under Python 2.
Under Python 3 it does nothing.

To support Python 2 and 3 with a single code base, define a __str__ method
returning text and apply this decorator to the class.

Backported from Django 1.5+.
"""
if not PY3:
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
redis<4.0
redis-py-cluster>=2.0.0
six
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
license="BSD",
packages=["redis_cache", "redis_cache.backends"],
description="Redis Cache Backend for Django",
install_requires=['redis<4.0', 'six'],
install_requires=['redis<4.0', 'redis-py-cluster>=2.0.0', 'six'],
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
Expand Down