示例#1
0
class ClusterSentinelCommands(SentinelCommandMixin):
    NODES_FLAGS = dict_merge(
        list_keys_to_dict([
            'SENTINEL GET-MASTER-ADDR-BY-NAME', 'SENTINEL MASTER',
            'SENTINEL MASTERS', 'SENTINEL MONITOR', 'SENTINEL REMOVE',
            'SENTINEL SENTINELS', 'SENTINEL SET', 'SENTINEL SLAVES'
        ], NodeFlag.BLOCKED))
示例#2
0
def test_list_keys_to_dict():
    def mock_true():
        return True

    assert list_keys_to_dict(["FOO", "BAR"], mock_true) == {
        "FOO": mock_true,
        "BAR": mock_true
    }
示例#3
0
class ClusterServerCommandMixin(ServerCommandMixin):
    NODES_FLAGS = dict_merge(
        list_keys_to_dict(['SHUTDOWN', 'SLAVEOF', 'CLIENT SETNAME'],
                          NodeFlag.BLOCKED),
        list_keys_to_dict(['FLUSHALL', 'FLUSHDB'], NodeFlag.ALL_MASTERS),
        list_keys_to_dict([
            'SLOWLOG LEN', 'SLOWLOG RESET', 'SLOWLOG GET', 'TIME', 'SAVE',
            'LASTSAVE', 'DBSIZE', 'CONFIG RESETSTAT', 'CONFIG REWRITE',
            'CONFIG GET', 'CONFIG SET', 'CLIENT KILL', 'CLIENT LIST',
            'CLIENT GETNAME', 'INFO', 'BGSAVE', 'BGREWRITEAOF'
        ], NodeFlag.ALL_NODES))

    RESULT_CALLBACKS = dict_merge(
        list_keys_to_dict([
            'CONFIG GET', 'CONFIG SET', 'SLOWLOG GET', 'CLIENT KILL', 'INFO',
            'BGREWRITEAOF', 'BGSAVE', 'CLIENT LIST', 'CLIENT GETNAME',
            'CONFIG RESETSTAT', 'CONFIG REWRITE', 'DBSIZE', 'LASTSAVE', 'SAVE',
            'SLOWLOG LEN', 'SLOWLOG RESET', 'TIME', 'FLUSHALL', 'FLUSHDB'
        ], lambda res: res))
示例#4
0
class ClusterScriptingCommandMixin(ScriptingCommandMixin):

    NODES_FLAGS = dict_merge({'SCRIPT KILL': NodeFlag.BLOCKED},
                             list_keys_to_dict([
                                 "SCRIPT LOAD",
                                 "SCRIPT FLUSH",
                                 "SCRIPT EXISTS",
                             ], NodeFlag.ALL_MASTERS))

    RESULT_CALLBACKS = {
        "SCRIPT LOAD": lambda res: list(res.values()).pop(),
        "SCRIPT EXISTS": lambda res: [all(k) for k in zip(*res.values())],
        "SCRIPT FLUSH": lambda res: all(res.values())
    }
示例#5
0
class CLusterPubSubCommandMixin(PubSubCommandMixin):
    
    NODES_FLAGS = dict_merge(
        list_keys_to_dict(
            ['PUBSUB CHANNELS', 'PUBSUB NUMSUB', 'PUBSUB NUMPAT'],
            NodeFlag.ALL_NODES
        )
    )

    RESULT_CALLBACKS = dict_merge(
        list_keys_to_dict([
            "PUBSUB CHANNELS",
        ], parse_cluster_pubsub_channels),
        list_keys_to_dict([
            "PUBSUB NUMSUB",
        ], parse_cluster_pubsub_numsub),
        list_keys_to_dict([
            "PUBSUB NUMPAT",
        ], parse_cluster_pubsub_numpat),
    )

    def pubsub(self, **kwargs):
        return ClusterPubSub(self.connection_pool, **kwargs)
示例#6
0
文件: keys.py 项目: steinitzu/aredis
class ClusterKeysCommandMixin(KeysCommandMixin):

    NODES_FLAGS = dict_merge(
        {
        'MOVE': NodeFlag.BLOCKED,
        'RANDOMKEY': NodeFlag.RANDOM,
        'SCAN': NodeFlag.ALL_MASTERS,
        },
        list_keys_to_dict(
            ['KEYS'],
            NodeFlag.ALL_NODES
        )
    )

    RESULT_CALLBACKS = {
        'KEYS': merge_result,
        'RANDOMKEY': first_key,
        'SCAN': lambda res: res
    }

    async def rename(self, src, dst):
        """
        Rename key ``src`` to ``dst``

        Cluster impl:
            This operation is no longer atomic because each key must be querried
            then set in separate calls because they maybe will change cluster node
        """
        if src == dst:
            raise ResponseError("source and destination objects are the same")

        data = await self.dump(src)

        if data is None:
            raise ResponseError("no such key")

        ttl = await self.pttl(src)

        if ttl is None or ttl < 1:
            ttl = 0

        await self.delete(dst)
        await self.restore(dst, ttl, data)
        await self.delete(src)

        return True

    async def delete(self, *names):
        """
        "Delete one or more keys specified by ``names``"

        Cluster impl:
            Iterate all keys and send DELETE for each key.
            This will go a lot slower than a normal delete call in StrictRedis.

            Operation is no longer atomic.
        """
        count = 0

        for arg in names:
            count += await self.execute_command('DEL', arg)

        return count

    async def renamenx(self, src, dst):
        """
        Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist

        Cluster impl:
            Check if dst key do not exists, then calls rename().

            Operation is no longer atomic.
        """
        if not await self.exists(dst):
            return await self.rename(src, dst)

        return False
示例#7
0
class ClusterCommandMixin:

    NODES_FLAGS = dict_merge(
        {
            'CLUSTER INFO': NodeFlag.ALL_NODES,
            'CLUSTER COUNTKEYSINSLOT': NodeFlag.SLOT_ID
        },
        list_keys_to_dict(['CLUSTER NODES', 'CLUSTER SLOTS'], NodeFlag.RANDOM))

    RESPONSE_CALLBACKS = {
        'CLUSTER ADDSLOTS': bool_ok,
        'CLUSTER COUNT-FAILURE-REPORTS': lambda x: int(x),
        'CLUSTER COUNTKEYSINSLOT': lambda x: int(x),
        'CLUSTER DELSLOTS': bool_ok,
        'CLUSTER FAILOVER': bool_ok,
        'CLUSTER FORGET': bool_ok,
        'CLUSTER INFO': parse_cluster_info,
        'CLUSTER KEYSLOT': lambda x: int(x),
        'CLUSTER MEET': bool_ok,
        'CLUSTER NODES': parse_cluster_nodes,
        'CLUSTER REPLICATE': bool_ok,
        'CLUSTER RESET': bool_ok,
        'CLUSTER SAVECONFIG': bool_ok,
        'CLUSTER SET-CONFIG-EPOCH': bool_ok,
        'CLUSTER SETSLOT': bool_ok,
        'CLUSTER SLAVES': parse_cluster_nodes,
        'CLUSTER SLOTS': parse_cluster_slots,
        'ASKING': bool_ok,
        'READONLY': bool_ok,
        'READWRITE': bool_ok,
    }

    RESULT_CALLBACKS = dict_merge(
        list_keys_to_dict([
            'CLUSTER INFO', 'CLUSTER ADDSLOTS',
            'CLUSTER COUNT-FAILURE-REPORTS', 'CLUSTER DELSLOTS',
            'CLUSTER FAILOVER', 'CLUSTER FORGET'
        ], lambda res: res))

    def _nodes_slots_to_slots_nodes(self, mapping):
        """
        Converts a mapping of
        {id: <node>, slots: (slot1, slot2)}
        to
        {slot1: <node>, slot2: <node>}

        Operation is expensive so use with caution
        """
        out = {}
        for node in mapping:
            for slot in node['slots']:
                out[str(slot)] = node['id']
        return out

    async def cluster_addslots(self, node_id, *slots):
        """
        Assign new hash slots to receiving node

        Sends to specefied node
        """
        return await self.execute_command('CLUSTER ADDSLOTS',
                                          *slots,
                                          node_id=node_id)

    async def cluster_count_failure_report(self, node_id=''):
        """
        Return the number of failure reports active for a given node

        Sends to specefied node
        """
        return await self.execute_command('CLUSTER COUNT-FAILURE-REPORTS',
                                          node_id=node_id)

    async def cluster_countkeysinslot(self, slot_id):
        """
        Return the number of local keys in the specified hash slot

        Send to node based on specefied slot_id
        """
        return await self.execute_command('CLUSTER COUNTKEYSINSLOT', slot_id)

    async def cluster_delslots(self, *slots):
        """
        Set hash slots as unbound in the cluster.
        It determines by it self what node the slot is in and sends it there

        Returns a list of the results for each processed slot.
        """
        cluster_nodes = self._nodes_slots_to_slots_nodes(await
                                                         self.cluster_nodes())
        res = list()
        for slot in slots:
            res.append(await self.execute_command('CLUSTER DELSLOTS',
                                                  slot,
                                                  node_id=cluster_nodes[slot]))
        return res

    async def cluster_failover(self, node_id, option):
        """
        Forces a slave to perform a manual failover of its master

        Sends to specefied node
        """
        if not isinstance(option, str) or option.upper() not in {
                'FORCE', 'TAKEOVER'
        }:
            raise ClusterError('Wrong option provided')
        return await self.execute_command('CLUSTER FAILOVER',
                                          option,
                                          node_id=node_id)

    async def cluster_forget(self, node_id):
        """
        remove a node via its node ID from the set of known nodes
        of the Redis Cluster node receiving the command

        Sends to all nodes in the cluster
        """
        return await self.execute_command('CLUSTER FORGET', node_id)

    async def cluster_info(self):
        """
        Provides info about Redis Cluster node state

        Sends to random node in the cluster
        """
        return await self.execute_command('CLUSTER INFO')

    async def cluster_keyslot(self, name):
        """
        Returns the hash slot of the specified key

        Sends to random node in the cluster
        """
        return await self.execute_command('CLUSTER KEYSLOT', name)

    async def cluster_meet(self, node_id, host, port):
        """
        Force a node cluster to handshake with another node.

        Sends to specefied node
        """
        return await self.execute_command('CLUSTER MEET',
                                          host,
                                          port,
                                          node_id=node_id)

    async def cluster_nodes(self):
        """
        Force a node cluster to handshake with another node

        Sends to random node in the cluster
        """
        return await self.execute_command('CLUSTER NODES')

    async def cluster_replicate(self, target_node_id):
        """
        Reconfigure a node as a slave of the specified master node

        Sends to specefied node
        """
        return await self.execute_command('CLUSTER REPLICATE', target_node_id)

    async def cluster_reset(self, node_id, soft=True):
        """
        Reset a Redis Cluster node

        If 'soft' is True then it will send 'SOFT' argument
        If 'soft' is False then it will send 'HARD' argument

        Sends to specefied node
        """
        option = 'SOFT' if soft else 'HARD'
        return await self.execute_command('CLUSTER RESET',
                                          option,
                                          node_id=node_id)

    async def cluster_reset_all_nodes(self, soft=True):
        """
        Send CLUSTER RESET to all nodes in the cluster

        If 'soft' is True then it will send 'SOFT' argument
        If 'soft' is False then it will send 'HARD' argument

        Sends to all nodes in the cluster
        """
        option = 'SOFT' if soft else 'HARD'
        res = list()
        for node in await self.cluster_nodes():
            res.append(await self.execute_command('CLUSTER RESET',
                                                  option,
                                                  node_id=node['id']))
        return res

    async def cluster_save_config(self):
        """
        Forces the node to save cluster state on disk

        Sends to all nodes in the cluster
        """
        return await self.execute_command('CLUSTER SAVECONFIG')

    async def cluster_set_config_epoch(self, node_id, epoch):
        """
        Set the configuration epoch in a new node

        Sends to specefied node
        """
        return await self.execute_command('CLUSTER SET-CONFIG-EPOCH',
                                          epoch,
                                          node_id=node_id)

    async def cluster_setslot(self, node_id, slot_id, state):
        """
        Bind an hash slot to a specific node

        Sends to specified node
        """
        if state.upper() in {'IMPORTING', 'MIGRATING', 'NODE'
                             } and node_id is not None:
            return await self.execute_command('CLUSTER SETSLOT', slot_id,
                                              state, node_id)
        elif state.upper() == 'STABLE':
            return await self.execute_command('CLUSTER SETSLOT', slot_id,
                                              'STABLE')
        else:
            raise RedisError('Invalid slot state: {0}'.format(state))

    async def cluster_get_keys_in_slot(self, slot_id, count):
        """
        Return local key names in the specified hash slot
        Sends to specified node
        """
        return await self.execute_command('CLUSTER GETKEYSINSLOT', slot_id,
                                          count)

    async def cluster_slaves(self, target_node_id):
        """
        Force a node cluster to handshake with another node

        Sends to targeted cluster node
        """
        return await self.execute_command('CLUSTER SLAVES', target_node_id)

    async def cluster_slots(self):
        """
        Get array of Cluster slot to node mappings

        Sends to random node in the cluster
        """
        return await self.execute_command('CLUSTER SLOTS')