Skip to content
This repository has been archived by the owner on Jan 9, 2024. It is now read-only.

readonly mode not execute command on slave node #339

Open
ididid8 opened this issue Oct 23, 2019 · 5 comments
Open

readonly mode not execute command on slave node #339

ididid8 opened this issue Oct 23, 2019 · 5 comments
Labels
3.0.0 All issues that will be looked at for 3.0.0 release gathering feedback type: bug

Comments

@ididid8
Copy link

ididid8 commented Oct 23, 2019

I set readonly_mode=True to test the readonly mode. And print the node which realy execute command. But whether readonly_mode is True or False the node server_type is all master.

from rediscluster import RedisCluster


def main():
    startup_nodes = [{"host": "", "port": ""}]
    rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True, readonly_mode=True)
    print(rc.set('foo', 'bar'))
    print(rc.get('foo'))

if __name__ == "__main__":
    main()

I print the node in client.py to get node info.

 @clusterdown_wrapper
    def execute_command(self, *args, **kwargs):
        """
        Send a command to a node in the cluster
        """
        ....
            else:
                if self.refresh_table_asap:
                    # MOVED
                    node = self.connection_pool.get_master_node_by_slot(slot)
                else:
                    node = self.connection_pool.get_node_by_slot(slot, self.read_from_replicas and (command in self.READ_COMMANDS))
                    is_read_replica = node['server_type'] == 'slave'
                print(node)
                r = self.connection_pool.get_connection_by_node(node)

        ....

The print is :

{'host': '', 'port': , 'name': '', 'server_type': 'master'}
True
{'host': '', 'port': , 'name': '', 'server_type': 'master'}
bar

I consider is because of the partial get node.

                if self.refresh_table_asap:
                    # MOVED
                    node = self.connection_pool.get_master_node_by_slot(slot)
                else:
                    node = self.connection_pool.get_node_by_slot(slot, self.read_from_replicas and (command in self.READ_COMMANDS))
                    is_read_replica = node['server_type'] == 'slave'
                r = self.connection_pool.get_connection_by_node(node)
@Grokzen
Copy link
Owner

Grokzen commented May 6, 2020

@ididid8 I did some digging into this issue, what i figured out is that there is two different connectionpools implemented that is controlled and activated by two different flags. What it seems like is that the flag you are using right now readonly_mode=True is using a broken implementation of READONLY mode. What is working tho is the following code snippet where i modified your example

from rediscluster import RedisCluster

startup_nodes = [{"host": "127.0.0.1", "port": "7000"}]
rc = RedisCluster(
    startup_nodes=startup_nodes,
    decode_responses=True,
    read_from_replicas=True,
)
print(rc.set('foo', 'bar'))

for i in range(0, 10):
    print(rc.get('foo'))

read_from_replicas=True activates a working connectionpool that will utilize READONLY correctly. When i ran the script above it loadbalanced the GET commands about equally between the master and the slave nodes to which the slot belonged to. No it do not send all read commands to the slaves, but it loadbalances them equally across all nodes at least. Both of these connection pools needs to be merged into a single solution, but right now they will remain as is until i can sort that out and i reccommend that you use read_from_replicas=True to fullfill your need.

@Grokzen Grokzen added the 3.0.0 All issues that will be looked at for 3.0.0 release label Sep 17, 2020
@Grokzen
Copy link
Owner

Grokzen commented Sep 17, 2020

The two different connection pools will get a makeover in 3.0.0

@duke-cliff
Copy link

I found this bug today as well, figured out I need to set readonly_mode=False and read_from_replicas =True

@duke-cliff
Copy link

@Grokzen There's another bug. Currently, the pipeline will never work on replicas, https://github.com/Grokzen/redis-py-cluster/blob/master/rediscluster/pipeline.py#L197.
I use pipelines for multiple reads instead of the mget hack. But it's not working with this library on scaling read.

@luoyucumt
Copy link

luoyucumt commented Dec 14, 2020

import random
from rediscluster.connection import ClusterWithReadReplicasConnectionPool
from rediscluster import RedisCluster

class ClusterWithOnlyReadReplicasConnectionPool(ClusterWithReadReplicasConnectionPool):
    def get_node_by_slot(self, slot, read_command=False):
        nodes_in_slot = self.nodes.slots[slot]
        # return slave node as possible
        if read_command:
            try:
                random_index = random.randrange(1, len(nodes_in_slot))
                return nodes_in_slot[random_index]
            except KeyError:
                pass
        return nodes_in_slot[0]

startup_nodes = [{"host": "127.0.0.1", "port": "7000"}]
pool = ClusterWithOnlyReadReplicasConnectionPool(
        startup_nodes=startup_nodes,
        decode_responses=True,
)
rc = RedisCluster(
    connection_pool=pool,
    read_from_replicas=True,
)

for i in range(0, 10):
    print(rc.get('foo'))

Use this code read from slave nodes

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
3.0.0 All issues that will be looked at for 3.0.0 release gathering feedback type: bug
Projects
None yet
Development

No branches or pull requests

4 participants