Redis punsubscribe not unsubscribing - node.js

I have one redis client for pub-sub. I'm using a websocket message handler to dynamically subscribe to a redis channel. The payload of the websocket message contains an ID that I use to create the channel-name. So for example lobby:${lobbyID}:joined.
Subscribing to this channel works fine, messages are received when publishing to that channel.
But the issue that I'm having is that I want to unsubscribe from this channel at one point. My assumption by reading the redis-documentation is that I would use punsubscribe so I can unsubscribe from any channels with the pattern lobby:*:joined, but messages are still received after trying that.
import redis from 'redis';
const subClient = redis.createClient();
subClient.on('message', (channel, message) => {
// Received message x on channel y
});
const socketHandlerSubscribe = (lobbyID) => {
subClient.subscribe(`lobby:${lobbyID}:joined`);
}
const socketHandlerUnsubscribe = () => {
subClient.punsubscribe('lobby:*:joined'); // true
}
When using the redis-cli the pattern seems valid when using PUBSUB CHANNEL lobby:*:joined. I could solve this issue by passing a lobby ID to the unsubscribe handler aswell, but punsubscribe should be the solution for it.
I also encountered this earlier with a scenario where I looped through an array of user ID's and created a subscription for each on statuses:${userID} and tried a punsubscribe on statuses:*, without any success.
Am I doing something wrong or this is an issue node-redis related? I'm using redis version 2.8.0

I noticed that there are two different types of subscriptions. On channels and patterns. In my question I was subscribing to a channel, and unsubscribing on a pattern, these two are not 'compatible' so this won't work.
I used nc to debug this, as redis-cli won't allow additional commands when entering subscribed state.

Related

io.to(targetSocketID).emit() is there a way to emit to just sender and receiver

I'm trying to create a private messaging functionality using socket.io with React and Node.
I am able to send a message to a particular socket like so:
io.to(targetSocketID).emit('privateMessage' message)
it successfully sends it to that specific user but not the sender. is there a way to just emit a message to sender and the target user to make this simple? From what I can see there is two approaches here.
When a message is created push the sender messages into a messages array setMessages([...messages, senderMessages]) and also push the socket message into that array setMessages([...messages, receivedMessages]). this approach seems sloppy and like to avoid this route as it can become problematic.
generate a unique room for each user and send the room to the server and join it:
//server
socket.on('joinRoom', room => {
socket.join(room)
socket.on('privateMessage', message => {
socket.on(room).emit('messageResponse', message)
})
})
I would like to know if there is a better way to do this.
that allows me to emit a message to just sender AND targeted receiver.

Unsubscribe from a specific queue using node-amqplib on reconnect

problem: remote systems reconnect to multiple nodes websocket server, for each system a dedicated queue in RabbitMQ is created/used. The queues should be automatically removed if no active connections exist. Websocket connect/disconnect events handlers are asynchronous, quite heavy, observed problem that a disconnect event handler finished after reconnect, making system inconsistent.
The main issue is with RabbitMQ queues - initial solution was to create unique queues for each connection and remove them on disconnect. Appeared to be heavy.
Second approach was to keep a dedicated queue per remote system (same queue name for any connection), the problem was that assertQueue added consumers for the same queue. Need to find way to remove stale queue consumers without removing the queue itself.
Solution is to store list of consumers per remote system and on disconnect event trigger cancel function with the olderst consumerTag, then update the list of queue consumers for the given remote system.
on remote system connect event
import { Replies } from "amqplib";
// bind callback function for queue-specific messages and store returned consumer description
const result: Replies.Consume = await channel.consume(queueName, this.onSomeMessage.bind(this));
// update consumers list for the connected remote system
const consumers: Array<string> | undefined = this.consumers.get(remoteId);
if (consumers === undefined) {
const consumersList: Array<string> = new Array();
consumersList.push(result.consumerTag);
this.consumers.set(remoteId, consumersList);
} else {
consumers.push(result.consumerTag);
}
on remote system disconnect event
// remove the oldest consumer in the list and update the list itself
// use cancel method of the amqp channel
const consumers = this.consumers.get(remoteId);
if (consumers === undefined) {
// shouldn't happen
console.error(`consumers list for ${remoteId} is empty`);
} else {
const consumerTag = consumers[0];
await this.rxchannel.addSetup(async (channel: ConfirmChannel) => {
await channel.cancel(consumerTag);
consumers.shift();
});
}
The code snippets are from some class' methods implementation (if you're wondering about "this").
Copyright notice (especially for German colleagues): the code from this answer can be used under Beerware (https://en.wikipedia.org/wiki/Beerware) or MIT license (whatever one prefers).

NodeJS server no http for Redis subscription

I want to develop a process that only subscribes to a Redis channel and stay alive to handle received messages.
I wrote the following code:
var redis = require("redis");
var sub = redis.createClient({host: process.env.REDIS_HOST});
console.log('subscribing...')
sub.on('subscribe', () => console.log('subscribed'));
sub.on('message', (ch, msg) => console.log(`Received message on ${ch}:${msg}`));
console.log('done')
But obviously it does not work: when launched it goes through all lines and dies. I think I don't need framework like Express because my process does not use http.
How could I write a server that stay alive "forever" without using http frameworks?
You're not subscribing to a channel:
sub.subscribe('channel');
I used the exact code above and the process stays open. In the code above you aren't publishing any messages, therefore you will only see "subscribing..." and "done" printed to the terminal.
Also, as mentioned you aren't subscribing to the channel either.

ZMQ pub/sub subscribe

I am having trouble figuring out how to subscribe to a particularly "channel" with ZMQ with regard to its pub/sub functionality.
Here is the publisher:
var zmq = require('zmq');
var pub = zmq.socket('pub');
pub.bindSync('tcp://127.0.0.1:5555');
setInterval(function(){
pub.send('pub msg');
},500);
here is the subscriber:
var sub = zmq.socket('sub');
sub.connect('tcp://127.0.0.1:5555');
sub.subscribe(''); //herein lies the question
sub.on('message',function(msg){
console.log('Received msg:',msg);
}
This works as is, but the problem is that if I change the argument to sub.subscribe to anything but an empty string (''), the subscriber doesn't receive any messages from the publisher.
How do I configure pub/sub with ZMQ correctly?
sub.subscribe('topic') adds a filter to your subscriber socket so that you only receive messages starting with the string topic. You can add multiple filters by calling it more than once. sub.subscribe('') removes any existing filter so your subscriber gets all messages sent by the publisher.
In your code using sub.subscribe('pub') would yield messages on the subscriber side.
The pub/sub example in the zeromq.node GitHub is a good place to look to understand how subscriptions work.

How to subscribe to multiple channels on Redis NodeJS

I have a function which I will call from time to time.
function blah() {
sub.unsubscribe();
sub.subscribe("a");
sub.subscribe("b");
}
Above results in error message "Error: node_redis command queue state error."
Same as https://github.com/mranney/node_redis/issues/137
Once a connection is subscribed, it can only issue subscription related commands (subscribe, psubscribe, etc)
Might another part of your code be using the same connection?
You could also try and subscribe to multiple channels with one call sub.subscribe("a", "b") or subscribe to a pattern that matches your need?
I am currently using redis.I was also facing the same issue in which i was trying to subscribe a set of channels.
var redis = require('redis')
const subscribe = redis.createClient({
host: 'localhost',
port: 6379
})
subscribe.psubscribe(`user:chat:*`)
subscribe.on('pmessage', function(pattern, channel, message) {
console.log(channel, message, pattern)
// Write Your Awesome Code here.
})
Psubscribe used for subscribing for multiple channels using redis. You can subscribe multiple channel on the basis of pattern.

Resources