node-redis-pubsub
node-redis-pubsub copied to clipboard
Crash undefined at line 39
Hi sometimes I have the following error:
TypeError: Cannot read property '_incommingMessage' of undefined at RedisClient.incommingMessage (/app/node_modules/rpsc/index.js:39:66) at RedisClient.emit (events.js:198:13) at return_pub_sub (/app/node_modules/redis/index.js:714:18) at RedisClient.return_reply (/app/node_modules/redis/index.js:761:9) at JavascriptRedisParser.returnReply (/app/node_modules/redis/index.js:137:18) at JavascriptRedisParser.execute (/app/node_modules/redis-parser/lib/parser.js:544:14) at Socket.
Is there a way to improve this ? What can cause the function to have an undefined within it's table ?
Thanks for the plug-in :-)
@Francois-Belanger I had a bit of a look at the code, which I have not looked at in many years unfortunately, and didn't have much luck reproducing it, or even working out exactly what the problem is, would you be able to help create an error test case? (or provide any further clues - eg what kind of frequency does this happen)
Hi @Jezternz I finally wrote my own version :-( Your plugin is a great inspiration. Instead of an array I used a Map and add lots of checks. If you would like I can share the code.
@Francois-Belanger that would be great, please :)
My pleasure, here:
import * as redis from 'redis';
let _pubClient: redis.RedisClient;
let rPubSubIdCounter = 1;
const clientLookup: Map<number /*Client Id*/, subClient> = new Map();
const globalSubscriptions: Map<string /*channel*/, number[] /*Array of client Id*/> = new Map();
export class pubSub {
constructor() {
}
init(pubClient: redis.RedisClient) {
_pubClient = pubClient;
_pubClient.on("message", this.incommingMessage);
}
createClient() {
const newClient = new subClient();
newClient.id = rPubSubIdCounter++;
clientLookup.set(newClient.id, newClient);
console.log('Create client id = ' + newClient.id);
console.log('Number of active subs : ', clientLookup.size);
return newClient;
};
incommingMessage(rawchannel: string, strMessage: string) {
if (globalSubscriptions.has(rawchannel)) {
const listClient = globalSubscriptions.get(rawchannel);
for (const clientId of listClient) {
if (clientLookup.has(clientId)) {
clientLookup.get(clientId)._incommingMessage(rawchannel, strMessage);
}
}
}
else {
//console.log(rawchannel+' channel had an incomming message with no listeners');
}
}
endClient(client: subClient) {
if (!client)
return;
console.log('end client id = ' + client.id + ' closing subscriptions=' + client.localSubscriptions.join(','));
for (const ch of client.localSubscriptions) {
client.unsubscribe(ch);
}
clientLookup.delete(client.id);
console.log('Number of active subs : ', clientLookup.size);
}
}
class subClient {
id: number;
localSubscriptions: string[] = [];
_incommingMessage = function (s1: string, s2: string) { };
subscribe(channel: string) {
console.log('client ' + this.id + ' subscribing to ' + channel);
if (!(globalSubscriptions.has(channel))) {
globalSubscriptions.set(channel, [this.id]);
_pubClient.subscribe(channel);
} else if (globalSubscriptions.get(channel).indexOf(this.id) == -1) {
globalSubscriptions.get(channel).push(this.id);
}
if (this.localSubscriptions.indexOf(channel) == -1) {
this.localSubscriptions.push(channel);
}
}
unsubscribe(channel: string) {
console.log('client ' + this.id + ' unsubscribing from ' + channel);
if (globalSubscriptions.has(channel)) {
const indx = globalSubscriptions.get(channel).indexOf(this.id);
if (indx != -1) {
globalSubscriptions.get(channel).splice(indx, 1);
if (globalSubscriptions.get(channel).length == 0) {
globalSubscriptions.delete(channel);
_pubClient.unsubscribe(channel);
}
}
}
const indx = this.localSubscriptions.indexOf(channel);
if (indx != -1) {
this.localSubscriptions.splice(indx, 1);
}
}
onMessage(msgFn: any) {
this._incommingMessage = msgFn;
}
}