|
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 ( |
@@ -153,52 +153,6 @@ def parse_cluster_shards(resp, **options): |
153 | 153 | ) |
154 | 154 | KWARGS_DISABLED_KEYS = ("host", "port") |
155 | 155 |
|
156 | | -# Not complete, but covers the major ones |
157 | | -# https://redis.io/commands |
158 | | -READ_COMMANDS = frozenset( |
159 | | - [ |
160 | | - "BITCOUNT", |
161 | | - "BITPOS", |
162 | | - "EXISTS", |
163 | | - "GEODIST", |
164 | | - "GEOHASH", |
165 | | - "GEOPOS", |
166 | | - "GEORADIUS", |
167 | | - "GEORADIUSBYMEMBER", |
168 | | - "GET", |
169 | | - "GETBIT", |
170 | | - "GETRANGE", |
171 | | - "HEXISTS", |
172 | | - "HGET", |
173 | | - "HGETALL", |
174 | | - "HKEYS", |
175 | | - "HLEN", |
176 | | - "HMGET", |
177 | | - "HSTRLEN", |
178 | | - "HVALS", |
179 | | - "KEYS", |
180 | | - "LINDEX", |
181 | | - "LLEN", |
182 | | - "LRANGE", |
183 | | - "MGET", |
184 | | - "PTTL", |
185 | | - "RANDOMKEY", |
186 | | - "SCARD", |
187 | | - "SDIFF", |
188 | | - "SINTER", |
189 | | - "SISMEMBER", |
190 | | - "SMEMBERS", |
191 | | - "SRANDMEMBER", |
192 | | - "STRLEN", |
193 | | - "SUNION", |
194 | | - "TTL", |
195 | | - "ZCARD", |
196 | | - "ZCOUNT", |
197 | | - "ZRANGE", |
198 | | - "ZSCORE", |
199 | | - ] |
200 | | -) |
201 | | - |
202 | 156 |
|
203 | 157 | def cleanup_kwargs(**kwargs): |
204 | 158 | """ |
@@ -1965,14 +1919,25 @@ def _send_cluster_commands( |
1965 | 1919 | # refer to our internal node -> slot table that |
1966 | 1920 | # tells us where a given |
1967 | 1921 | # command should route to. |
1968 | | - node = self._determine_nodes(*c.args) |
| 1922 | + passed_targets = c.options.pop("target_nodes", None) |
| 1923 | + if passed_targets and not self._is_nodes_flag(passed_targets): |
| 1924 | + target_nodes = self._parse_target_nodes(passed_targets) |
| 1925 | + else: |
| 1926 | + target_nodes = self._determine_nodes(*c.args, node_flag=passed_targets) |
| 1927 | + if not target_nodes: |
| 1928 | + raise RedisClusterException( |
| 1929 | + f"No targets were found to execute {c.args} command on" |
| 1930 | + ) |
| 1931 | + if len(target_nodes) > 1: |
| 1932 | + raise RedisClusterException(f"Too many targets for command {c.args}") |
1969 | 1933 |
|
| 1934 | + node = target_nodes[0] |
1970 | 1935 | # now that we know the name of the node |
1971 | 1936 | # ( it's just a string in the form of host:port ) |
1972 | 1937 | # we can build a list of commands for each node. |
1973 | | - node_name = node[0].name |
| 1938 | + node_name = node.name |
1974 | 1939 | if node_name not in nodes: |
1975 | | - redis_node = self.get_redis_connection(node[0]) |
| 1940 | + redis_node = self.get_redis_connection(node) |
1976 | 1941 | connection = get_connection(redis_node, c.args) |
1977 | 1942 | nodes[node_name] = NodeCommands( |
1978 | 1943 | redis_node.parse_response, redis_node.connection_pool, connection |
|
0 commit comments