in redis/cluster.py [0:0]
def determine_slot(self, *args):
"""
Figure out what slot to use based on args.
Raises a RedisClusterException if there's a missing key and we can't
determine what slots to map the command to; or, if the keys don't
all map to the same key slot.
"""
command = args[0]
if self.command_flags.get(command) == SLOT_ID:
# The command contains the slot ID
return args[1]
# Get the keys in the command
# EVAL and EVALSHA are common enough that it's wasteful to go to the
# redis server to parse the keys. Besides, there is a bug in redis<7.0
# where `self._get_command_keys()` fails anyway. So, we special case
# EVAL/EVALSHA.
if command.upper() in ("EVAL", "EVALSHA"):
# command syntax: EVAL "script body" num_keys ...
if len(args) <= 2:
raise RedisClusterException(f"Invalid args in command: {args}")
num_actual_keys = int(args[2])
eval_keys = args[3 : 3 + num_actual_keys]
# if there are 0 keys, that means the script can be run on any node
# so we can just return a random slot
if len(eval_keys) == 0:
return random.randrange(0, REDIS_CLUSTER_HASH_SLOTS)
keys = eval_keys
else:
keys = self._get_command_keys(*args)
if keys is None or len(keys) == 0:
# FCALL can call a function with 0 keys, that means the function
# can be run on any node so we can just return a random slot
if command.upper() in ("FCALL", "FCALL_RO"):
return random.randrange(0, REDIS_CLUSTER_HASH_SLOTS)
raise RedisClusterException(
"No way to dispatch this command to Redis Cluster. "
"Missing key.\nYou can execute the command by specifying "
f"target nodes.\nCommand: {args}"
)
# single key command
if len(keys) == 1:
return self.keyslot(keys[0])
# multi-key command; we need to make sure all keys are mapped to
# the same slot
slots = {self.keyslot(key) for key in keys}
if len(slots) != 1:
raise RedisClusterException(
f"{command} - all keys must map to the same key slot"
)
return slots.pop()