node-redis-pubsub icon indicating copy to clipboard operation
node-redis-pubsub copied to clipboard

Crash undefined at line 39

Open Francois-Belanger opened this issue 3 years ago • 4 comments

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. (/app/node_modules/redis/index.js:218:27) at Socket.emit (events.js:198:13) at addChunk (_stream_readable.js:288:12) at readableAddChunk (_stream_readable.js:269:11)

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 avatar Oct 10 '20 00:10 Francois-Belanger

@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)

Jezternz avatar Dec 08 '20 11:12 Jezternz

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 avatar Dec 08 '20 11:12 Francois-Belanger

@Francois-Belanger that would be great, please :)

Jezternz avatar Dec 09 '20 02:12 Jezternz

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;
    }
}

Francois-Belanger avatar Dec 09 '20 02:12 Francois-Belanger