def _transfer_slots(redis_conn_from: Redis, redis_id_from: str, redis_conn_to: Redis, redis_id_to: str, slots: list):
        """
        Documentation from http://redis.io/commands/cluster-setslot
         1. Set the destination node slot to importing state using CLUSTER SETSLOT <slot> IMPORTING <source-node-id>.
         2. Set the source node slot to migrating state using CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>.
         3. Get keys from the source node with CLUSTER GETKEYSINSLOT command and move them into the destination node
            using the MIGRATE command.
         4. Use CLUSTER SETSLOT <slot> NODE <destination-node-id> in the source or destination.
        """
        print('Transfering %d slots from %s to %s...' % (len(slots), redis_id_from, redis_id_to))
        dest_host = redis_conn_to.connection_pool.connection_kwargs['host']
        dest_port = redis_conn_to.connection_pool.connection_kwargs['port']

        pipeline_to = redis_conn_to.pipeline()
        pipeline_from = redis_conn_from.pipeline()
        for slot in slots:
            # 1, 2
            pipeline_to.execute_command('CLUSTER SETSLOT', slot, 'IMPORTING', redis_id_from)
            pipeline_from.execute_command('CLUSTER SETSLOT', slot, 'MIGRATING', redis_id_to)
        pipeline_to.execute()
        pipeline_from.execute()

        for slot in slots:
            # 3
            keys = redis_conn_from.execute_command('CLUSTER GETKEYSINSLOT', slot, 1000000)
            if len(keys) > 0:
                redis_conn_from.execute_command('MIGRATE', dest_host, dest_port, "", 0, 180000, 'KEYS', *keys)
            # 4
            redis_conn_to.execute_command('CLUSTER SETSLOT', slot, 'NODE', redis_id_to)
示例#2
0
    def _add_new_nodes(self, cluster_size):
        old_nodes = self.nodes.copy()
        nodes_before = self._get_nodes_primitive()
        self._docker_scale(cluster_size)
        nodes_after = self._get_nodes_primitive()

        new_ips = [
            ':'.join(map(str, x)) for x in set(nodes_after) - set(nodes_before)
        ]
        print(new_ips)
        master_ip_port = old_nodes[0]['ip_port']
        master_ip, master_port = master_ip_port.split(':')
        master_conn = Redis(master_ip, master_port)

        print("Adding nodes to the cluster")
        for ip in new_ips:
            new_ip, new_port = ip.split(':')
            master_conn.execute_command('CLUSTER MEET', new_ip, new_port)

        print("Preventive fix")
        sleep(3)
        fix = subprocess.Popen(
            ['ruby', 'redis-trib.rb', 'fix', master_ip_port],
            stdin=subprocess.PIPE,
            stdout=subprocess.DEVNULL)
        fix.communicate(b'yes\n')
        fix.wait()
        sleep(3)

        new_nodes = [
            x for x in self._get_running_nodes() if x['ip_port'] in new_ips
        ]
        slots_per_node = round(16384 / cluster_size)

        old_redises = {
            x[0]: Redis(x[0], x[1])
            for x in (y['ip_port'].split(':') for y in old_nodes)
        }
        new_redises = [
            Redis(x[0], x[1])
            for x in (y['ip_port'].split(':') for y in new_nodes)
        ]
        slots_repartition = self._get_slots_repartition(
            list(old_redises.values())[0])

        for dest_node, dest_redis, i in zip(new_nodes, new_redises,
                                            range(len(new_nodes))):
            slots = slots_repartition[i * slots_per_node:(i + 1) *
                                      slots_per_node]
            sources_ip = {x[1] for x in slots}
            for source_ip in sources_ip:
                slots_for_source = [x for x in slots if x[1] == source_ip]
                source_redis = old_redises[source_ip]
                self._transfer_slots(source_redis, slots_for_source[0][3],
                                     dest_redis, dest_node['id'],
                                     [x[0] for x in slots_for_source])

        subprocess.check_call(
            ['ruby', 'redis-trib.rb', 'info', master_ip_port])
    def _add_new_nodes(self, cluster_size):
        old_nodes = self.nodes.copy()
        nodes_before = self._get_nodes_primitive()
        self._docker_scale(cluster_size)
        nodes_after = self._get_nodes_primitive()

        new_ips = [':'.join(map(str, x)) for x in set(nodes_after) - set(nodes_before)]
        print(new_ips)
        master_ip_port = old_nodes[0]['ip_port']
        master_ip, master_port = master_ip_port.split(':')
        master_conn = Redis(master_ip, master_port)

        print("Adding nodes to the cluster")
        for ip in new_ips:
            new_ip, new_port = ip.split(':')
            master_conn.execute_command('CLUSTER MEET', new_ip, new_port)

        print("Preventive fix")
        sleep(3)
        fix = subprocess.Popen(['ruby', 'redis-trib.rb', 'fix', master_ip_port], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL)
        fix.communicate(b'yes\n')
        fix.wait()
        sleep(3)

        new_nodes = [x for x in self._get_running_nodes() if x['ip_port'] in new_ips]
        slots_per_node = round(16384 / cluster_size)

        old_redises = {x[0]: Redis(x[0], x[1]) for x in (y['ip_port'].split(':') for y in old_nodes)}
        new_redises = [Redis(x[0], x[1]) for x in (y['ip_port'].split(':') for y in new_nodes)]
        slots_repartition = self._get_slots_repartition(list(old_redises.values())[0])

        for dest_node, dest_redis, i in zip(new_nodes, new_redises, range(len(new_nodes))):
            slots = slots_repartition[i * slots_per_node: (i + 1) * slots_per_node]
            sources_ip = {x[1] for x in slots}
            for source_ip in sources_ip:
                slots_for_source = [x for x in slots if x[1] == source_ip]
                source_redis = old_redises[source_ip]
                self._transfer_slots(source_redis, slots_for_source[0][3],
                                     dest_redis, dest_node['id'],
                                     [x[0] for x in slots_for_source])

        subprocess.check_call(['ruby', 'redis-trib.rb', 'info', master_ip_port])
 def _get_slots_repartition(any_redis_conn: Redis):
     """
     Returns a shuffled list of (slot_number, node_ip, node_port, node_id)
     """
     # List of [10923, 16383, [b'10.0.0.4', 6379, b'f1dc21d0b7a24aaea3b3fcd0ef943a35fa2ebb42']]
     cluster_slots = any_redis_conn.execute_command('CLUSTER SLOTS')
     output = []
     for slot in cluster_slots:
         for i in range(slot[0], slot[1] + 1):
             output.append((i, slot[2][0].decode(), slot[2][1], slot[2][2].decode()))
     random.shuffle(output)
     return output
示例#5
0
 def _get_slots_repartition(any_redis_conn: Redis):
     """
     Returns a shuffled list of (slot_number, node_ip, node_port, node_id)
     """
     # List of [10923, 16383, [b'10.0.0.4', 6379, b'f1dc21d0b7a24aaea3b3fcd0ef943a35fa2ebb42']]
     cluster_slots = any_redis_conn.execute_command('CLUSTER SLOTS')
     output = []
     for slot in cluster_slots:
         for i in range(slot[0], slot[1] + 1):
             output.append(
                 (i, slot[2][0].decode(), slot[2][1], slot[2][2].decode()))
     random.shuffle(output)
     return output
示例#6
0
    def _transfer_slots(redis_conn_from: Redis, redis_id_from: str,
                        redis_conn_to: Redis, redis_id_to: str, slots: list):
        """
        Documentation from http://redis.io/commands/cluster-setslot
         1. Set the destination node slot to importing state using CLUSTER SETSLOT <slot> IMPORTING <source-node-id>.
         2. Set the source node slot to migrating state using CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>.
         3. Get keys from the source node with CLUSTER GETKEYSINSLOT command and move them into the destination node
            using the MIGRATE command.
         4. Use CLUSTER SETSLOT <slot> NODE <destination-node-id> in the source or destination.
        """
        print('Transfering %d slots from %s to %s...' %
              (len(slots), redis_id_from, redis_id_to))
        dest_host = redis_conn_to.connection_pool.connection_kwargs['host']
        dest_port = redis_conn_to.connection_pool.connection_kwargs['port']

        pipeline_to = redis_conn_to.pipeline()
        pipeline_from = redis_conn_from.pipeline()
        for slot in slots:
            # 1, 2
            pipeline_to.execute_command('CLUSTER SETSLOT', slot, 'IMPORTING',
                                        redis_id_from)
            pipeline_from.execute_command('CLUSTER SETSLOT', slot, 'MIGRATING',
                                          redis_id_to)
        pipeline_to.execute()
        pipeline_from.execute()

        for slot in slots:
            # 3
            keys = redis_conn_from.execute_command('CLUSTER GETKEYSINSLOT',
                                                   slot, 1000000)
            if len(keys) > 0:
                redis_conn_from.execute_command('MIGRATE', dest_host,
                                                dest_port, "", 0, 180000,
                                                'KEYS', *keys)
            # 4
            redis_conn_to.execute_command('CLUSTER SETSLOT', slot, 'NODE',
                                          redis_id_to)