|
9 | 9 | from typing import Any, Callable, Dict, Tuple |
10 | 10 |
|
11 | 11 | from redis.client import CaseInsensitiveDict, PubSub, Redis, parse_scan |
12 | | -from redis.commands import CommandsParser, RedisClusterCommands |
| 12 | +from redis.commands import READ_COMMANDS, CommandsParser, RedisClusterCommands |
13 | 13 | from redis.connection import ConnectionPool, DefaultParser, Encoder, parse_url |
14 | 14 | from redis.crc import REDIS_CLUSTER_HASH_SLOTS, key_slot |
15 | 15 | from redis.exceptions import ( |
@@ -154,52 +154,6 @@ def parse_cluster_shards(resp, **options): |
154 | 154 | ) |
155 | 155 | KWARGS_DISABLED_KEYS = ("host", "port") |
156 | 156 |
|
157 | | -# Not complete, but covers the major ones |
158 | | -# https://redis.io/commands |
159 | | -READ_COMMANDS = frozenset( |
160 | | - [ |
161 | | - "BITCOUNT", |
162 | | - "BITPOS", |
163 | | - "EXISTS", |
164 | | - "GEODIST", |
165 | | - "GEOHASH", |
166 | | - "GEOPOS", |
167 | | - "GEORADIUS", |
168 | | - "GEORADIUSBYMEMBER", |
169 | | - "GET", |
170 | | - "GETBIT", |
171 | | - "GETRANGE", |
172 | | - "HEXISTS", |
173 | | - "HGET", |
174 | | - "HGETALL", |
175 | | - "HKEYS", |
176 | | - "HLEN", |
177 | | - "HMGET", |
178 | | - "HSTRLEN", |
179 | | - "HVALS", |
180 | | - "KEYS", |
181 | | - "LINDEX", |
182 | | - "LLEN", |
183 | | - "LRANGE", |
184 | | - "MGET", |
185 | | - "PTTL", |
186 | | - "RANDOMKEY", |
187 | | - "SCARD", |
188 | | - "SDIFF", |
189 | | - "SINTER", |
190 | | - "SISMEMBER", |
191 | | - "SMEMBERS", |
192 | | - "SRANDMEMBER", |
193 | | - "STRLEN", |
194 | | - "SUNION", |
195 | | - "TTL", |
196 | | - "ZCARD", |
197 | | - "ZCOUNT", |
198 | | - "ZRANGE", |
199 | | - "ZSCORE", |
200 | | - ] |
201 | | -) |
202 | | - |
203 | 157 |
|
204 | 158 | def cleanup_kwargs(**kwargs): |
205 | 159 | """ |
@@ -1993,14 +1947,25 @@ def _send_cluster_commands( |
1993 | 1947 | # refer to our internal node -> slot table that |
1994 | 1948 | # tells us where a given |
1995 | 1949 | # command should route to. |
1996 | | - node = self._determine_nodes(*c.args) |
| 1950 | + passed_targets = c.options.pop("target_nodes", None) |
| 1951 | + if passed_targets and not self._is_nodes_flag(passed_targets): |
| 1952 | + target_nodes = self._parse_target_nodes(passed_targets) |
| 1953 | + else: |
| 1954 | + target_nodes = self._determine_nodes(*c.args, node_flag=passed_targets) |
| 1955 | + if not target_nodes: |
| 1956 | + raise RedisClusterException( |
| 1957 | + f"No targets were found to execute {c.args} command on" |
| 1958 | + ) |
| 1959 | + if len(target_nodes) > 1: |
| 1960 | + raise RedisClusterException(f"Too many targets for command {c.args}") |
1997 | 1961 |
|
| 1962 | + node = target_nodes[0] |
1998 | 1963 | # now that we know the name of the node |
1999 | 1964 | # ( it's just a string in the form of host:port ) |
2000 | 1965 | # we can build a list of commands for each node. |
2001 | | - node_name = node[0].name |
| 1966 | + node_name = node.name |
2002 | 1967 | if node_name not in nodes: |
2003 | | - redis_node = self.get_redis_connection(node[0]) |
| 1968 | + redis_node = self.get_redis_connection(node) |
2004 | 1969 | connection = get_connection(redis_node, c.args) |
2005 | 1970 | nodes[node_name] = NodeCommands( |
2006 | 1971 | redis_node.parse_response, redis_node.connection_pool, connection |
|
0 commit comments