Skip to content

Instantly share code, notes, and snippets.

@aputs
Created September 15, 2018 05:41
Show Gist options
  • Save aputs/ecfa285199e202be8ad79d5d2db8ddf1 to your computer and use it in GitHub Desktop.
Save aputs/ecfa285199e202be8ad79d5d2db8ddf1 to your computer and use it in GitHub Desktop.
add support for redis sentinel on django-redis
# -*- coding: utf-8 -*-
import logging
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from redis.sentinel import Sentinel
from django_redis.client import DefaultClient
DJANGO_REDIS_LOGGER = getattr(settings, "DJANGO_REDIS_LOGGER", False)
DJANGO_REDIS_CLOSE_CONNECTION = getattr(settings,
"DJANGO_REDIS_CLOSE_CONNECTION", False)
class SentinelClient(DefaultClient):
"""
Sentinel client object extending django-redis DefaultClient
"""
def __init__(self, server, params, backend):
"""
Slightly different logic than connection to multiple Redis servers.
Reserve only one write and read descriptors, as they will be closed on exit anyway.
"""
super(SentinelClient, self).__init__(server, params, backend)
self._client = None
self._connection_string = server
self.log = logging.getLogger((DJANGO_REDIS_LOGGER or __name__))
def parse_connection_string(self, constring):
"""
Parse connection string in format:
master_name/sentinel_server:port,sentinel_server:port/db_id
Returns master name, list of tuples with pair (host, port) and db_id
"""
try:
connection_params = constring.split('://')
if len(connection_params) > 1:
connection_params = connection_params[1].split('/')
master_name = connection_params[0]
servers = [
host_port.split(':')
for host_port in connection_params[1].split(',')
]
sentinel_hosts = [(host, int(port)) for host, port in servers]
db = connection_params[2]
except (ValueError, TypeError, IndexError):
raise ImproperlyConfigured("Incorrect format '%s'" % (constring))
return master_name, sentinel_hosts, db
def get_client(self, write=True, tried=(), show_index=False):
"""
Method used to obtain a raw redis client.
This function is used by almost all cache backend
operations to obtain a native redis client/connection
instance.
"""
if self._client is None:
self._client = self.connect()
if show_index:
return self._client, 0
else:
return self._client
def connect(self, index=0):
"""
Creates a redis connection with connection pool.
"""
master_name, sentinel_hosts, db = self.parse_connection_string(
self._connection_string)
sentinel_timeout = self._options.get('SENTINEL_TIMEOUT', 1)
password = self._options.get('PASSWORD', None)
sentinel = Sentinel(
sentinel_hosts, socket_timeout=sentinel_timeout, password=password)
return sentinel.master_for(
master_name,
socket_timeout=sentinel_timeout,
db=db,
)
def close(self, **kwargs):
"""
Closing old connections, as master may change in time of inactivity.
"""
if DJANGO_REDIS_CLOSE_CONNECTION and self._client is not None:
del self._client
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment