Repository navigation
Description
Summary
Against a Redis Cluster, rvl index listall exits with status 1 and prints:
No way to dispatch this command to Redis Cluster. Missing key.
You can execute the command by specifying target nodes.
Command: ('FT._LIST',)
Other rvl index subcommands work against the same cluster. rvl index info -i <name>, for example, goes through SearchIndex. listall is the only one that bypasses it.
Reproduction
Start a Redis Cluster with the Search module (Redis 8.x).
Run:
rvl index listall --url "redis://127.0.0.1:7201?cluster=true"
The --json variant fails the same way, with nothing on stdout.
The CLI effectively does this:
from redis.cluster import RedisCluster
client = RedisCluster.from_url("redis://127.0.0.1:7201")
client.execute_command("FT._LIST") # RedisClusterException: Missing key
# Sending it to one node works and returns the full index list:
client.execute_command("FT._LIST", target_nodes=client.get_default_node())
# [b'my_index']
Expected behaviour: the command prints the indexes in the cluster, as it does against a standalone Redis.
Actual behaviour: it exits with status 1 and prints the RedisClusterException above to stderr.
Cause
Index.listall() in redisvl/cli/index.py opens its own connection and sends the command directly:
conn = RedisConnectionFactory.get_redis_connection(redis_url=redis_url)
indices = convert_bytes(conn.execute_command("FT._LIST"))
FT._LIST takes no key. redis-py 7.x and 8.x route keyless search commands using a static policy table (redis/commands/policies.py), and _list isn’t in that table’s ft section. So redis-py falls back to slot routing by key, finds no key, and raises. Sync SearchIndex.listall() behaves the same way on main: it worked on redis-py 6.4 and failed on 7.4 and 8.1.
Related: #795 fixes SearchIndex.listall() and AsyncSearchIndex.listall() on a cluster by sending FT._LIST to client.get_default_node(). The CLI doesn’t call either method, so it stays broken after #795 merges.
Suggested fix
Have the CLI list indexes through SearchIndex.listall(), so it uses the same cluster routing. There’s already a helper that does this: redisvl.migration.utils.list_indexes() builds a placeholder SearchIndex and calls listall(). The CLI can call it, or the helper can move to a neutral module such as redisvl/redis/utils.py so cli/index.py doesn’t import from migration/.
Ordering: this should land after #795. The change doesn’t touch #795’s files, but the CLI only works on a cluster once #795’s listall() routing is on main.
Tests: add a unit test that runs rvl index listall (with and without --json) and checks that it calls SearchIndex.listall() and prints the result.
Activity