ssubscribe within a cluster is not working properly
Hi! It seems there is an issue with the spublish()/ssubscribe() methods added in https://github.com/luin/ioredis/commit/6285e80ffb47564dc01d8e9940ff9a103bf70e2d:
import { Cluster } from "ioredis";
const clusterNodes = [
{
host: "localhost",
port: 7000,
},
{
host: "localhost",
port: 7001,
},
{
host: "localhost",
port: 7002,
},
{
host: "localhost",
port: 7003,
},
{
host: "localhost",
port: 7004,
},
{
host: "localhost",
port: 7005,
},
];
const pubClient = new Cluster(clusterNodes);
const subClient = pubClient.duplicate();
subClient.ssubscribe("foo", () => {
pubClient.spublish("foo", "bar");
});
subClient.on("smessage", (_, message) => {
console.log("got", message); // never received
});
The smessage event is not received. It works with classic publish()/subscribe() though:
subClient.subscribe("foo", () => {
pubClient.publish("foo", "bar");
});
subClient.on("message", (_, message) => {
console.log("got", message); // prints "got bar"
});
My docker-compose.yml, for reproducibility:
services:
redis-cluster:
image: grokzen/redis-cluster:7.0.10
ports:
- "7000-7005:7000-7005"
The test case here looks a bit weird, shouldn't it be something like:
const pub = new Cluster(options);
const ssub = new Cluster(options);
ssub.ssubscribe("test cluster", function () {
pub.spublish("test shard channel", "hi");
// node1.write(node1.findClientByName("ioredis-cluster(subscriber)"), [
// "smessage",
// "test shard channel",
// "hi",
// ]);
});
Thanks in advance!
Thanks for raising this up! Yeah we currently don't route messages to the right node and also don't subscribe to the right node. Should think about a solution for that.
I'm facing the same problem, I'm not getting the 'smessage' event when I register the sSubscribe function.
With a total of 3 Redis servers (ports 6379, 6380, 6381) deployed in a Redis Cluster using Docker Compose, I executed the code below.
import { Cluster, Redis } from 'ioredis'
async function getRedisClient(): Promise<Cluster> {
return new Promise(async (resolve, reject) => {
const redisClient = new Redis.Cluster([
{
host: 'localhost',
port: 6379
}
], {
scaleReads: 'slave',
retryDelayOnMoved: 100,
redisOptions: {
username: 'default',
password: 'redispw',
tls: undefined
},
})
redisClient.on('error', (err: Error) => {
console.error('[Ioredis] on ERROR', err)
})
redisClient.on('connect', async () => {
console.log('Redis cluster connected')
resolve(redisClient)
})
})
}
async function main() {
const publisher = await getRedisClient()
const subscriber = await getRedisClient()
subscriber.on('smessage', (channel: string, message: string) => {
console.log('smessage', channel, message)
})
subscriber.on('message', (channel: string, message: string) => {
console.log('message', channel, message)
})
await subscriber.ssubscribe('hello1') // Too many redirection error (127.0.0.1:6380)
await subscriber.ssubscribe('hello2') // Too many redirection error (127.0.0.1:6381)
await subscriber.ssubscribe('hello3') // ok (127.0.0.1:6379)
await publisher.spublish('hello3', '{"name": "honsemiro"}')
}
main()
Of these, only the 'hello3' channel was successful in subscribing to the shard channel. My guess is that there are different shards that are assigned based on the checksum value, and that's why it subscribes to certain shards and not others. Here's the error message.
Redis cluster connected
Redis cluster connected
c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\cluster\index.js:508
handlers.maxRedirections(new Error("Too many Cluster redirections. Last error: " + error));
^
Error: Too many Cluster redirections. Last error: ReplyError: MOVED 11613 127.0.0.1:6381
at EventEmitter.handleError (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\cluster\index.js:508:38)
at Command.command.reject (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\cluster\index.js:362:23)
at EventEmitter.handleReconnection (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\Redis.js:509:30)
at DataHandler.returnError (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\DataHandler.js:41:20)
at JavascriptRedisParser.returnError (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\DataHandler.js:15:22)
at JavascriptRedisParser.execute (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\redis-parser\lib\parser.js:542:14)
at Socket.<anonymous> (c:\Users\<redacted>\Documents\nodejs\tsc_test_01\node_modules\ioredis\built\DataHandler.js:25:20)
at Socket.emit (node:events:513:28)
at Socket.emit (node:domain:489:12)
at addChunk (node:internal/streams/readable:315:12)
is there any updates on this? @luin are there any workarounds until this will be supported?
If it's helpful to anyone reading this -- the library node-redis (v4) supports sharded pubsub commands properly (SSUBSCRIBE, SPUBLISH, SUNSUBCRIBE)
For my project I had to migrate off of ioredis and use node-redis instead because of this issue with ioredis not supporting sharded pubsub commands
Any news about it ? I'd prefer not to migrate to node-redis.
@tominou we ended up using node-redis for this part