4

I have setup a Redis replica with one master and two slaves with Redis Sentinel monitoring each of the nodes. I'm using redis-py library to communicate with master and slaves. Here is the code that I use to connect with master and slaves:

from redis.sentinel import Sentinel

sentinel = Sentinel([("localhost", 5000)])  # connect to one of the sentinels
master = sentinel.master_for("mymaster")  # get master object
slave = sentinel.slave_for("mymaster")  # get slave object

Now, I would use master for write and slave for reads. But the problem is that when failover occurs, the master object needs to be refreshed in order to point to a new master. For that, as mentioned in this answer and the docs, I'm supposed to subscribe on +switch-master channel to receive the address of new master.

But the questions is that, using which object am I supposed to subscribe to that channel? I tried this using master object as following:

ps = master.pubsub()
ps.subscribe("+switch-master")

And when the master was down, I tried getting the message as following:

ps.get_message()

But this does not return the expected message because the master itself is down I think.

The sentinel object does not support the pubsub.

>>> sentinel.pubsub()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Sentinel' object has no attribute 'pubsub'

As an alternative, in the linked answer it's suggested to use client-reconfig-script. But how am I supposed to modify an object in my application using an external script?

What am I missing here? I am new to Redis, so any reference would be appreciated.

Kaushal28
  • 5,377
  • 5
  • 41
  • 72
  • Hey @Kaushal28 did you solve this problem? I'm also stuck here, trying to update the redis master object but not luck – rhoitjadhav Jun 02 '22 at 10:21

1 Answers1

0

I'm not sure, but maybe something like this can help

class RedisWithReloadTest(Redis):

    def __init__(self, *args, **kwargs):
        super(RedisWithReloadTest, self).__init__(*args, **kwargs)
        self.sentinel_master_name = None

    def execute_command(self, *args, **kwargs):
        reload = kwargs.pop("reload", False)
        try:
            super().execute_command(*args, **kwargs)
        except ReadOnlyError:
            if reload:
                raise
            master = self.connection_pool.sentinel_manager.master_for(
                self.sentinel_master_name, redis_class=self.__class__
            )
            master.sentinel_master_name = self.sentinel_master_name
            kwargs.update(reload=reload)
            master.execute_command(*args, **kwargs)
            self.__dict__.update(master.__dict__)

    @classmethod
    def from_sentinel(cls, list_of_hosts_and_ports: List[tuple],    master: str):
        sentinel = Sentinel(list_of_hosts_and_ports)
        master = sentinel.master_for(master, redis_class=cls)
        master.app = app
        master.sentinel_master_name = master
        return master
Денис
  • 23
  • 5
  • Add some explanations through code comments or otherwise so that others can understand it. – Bunny Oct 17 '22 at 13:10
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Bunny Oct 17 '22 at 13:11