socket.io-redis-adapter: Big memory leak .clients() and .allRooms()

So, I recently rewrote some of our network code to support multiple nodes with the help of this library.

I used adapter.clients() and adapter.allRooms() only, and in a single place in the code each.

Everything worked just fine in dev, but once I put it into production (thousands of connections) a massive memory leak became apparent.

I spent the better part of today trying to track the leak down thinking it was something to do with the way I wrote the new code. After trying everything, I removed adapter.clients() and adapter.allRooms() and instead just used the standard socket.io .rooms object to get the channel names and socket ids, losing multi-node functionality, but completely getting rid of the memory leak. I removed the functions one at a time and only after both were gone did the leak disappear.

I’m curious if anyone is using these functions in a high traffic production environment (the functions get called A LOT) successfully, because currently it doesn’t seem possible.

I would be interested in looking into this leak further when I have some time, but am not very experienced in tracking down memory leaks, or very knowledgable about the inner workings of this library.

Any insight would be greatly appreciated.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 28 (8 by maintainers)

Most upvoted comments

Hi @fas3r

Yes, it’s a good idea to avoid allRooms and clients all together We realized that every time these calls are made, the entire socket.io cluster generates a lot of chatter. All nodes are requested for which rooms/clients they are managing, which they respond to and the data is collected before returned.

If you want speedy retrieval of this information, you are highly advised to keep the rooms/clients in custom data structures in redis. Just set keys on socket init & delete on disconnect. This is how we solved our performance problems

10,000 clients with several socket.io nodes will work just fine

@darrachequesne All good still! Can confirm that this is solved. Thanks everyone!

@webeks Yep

    io.of('/').adapter.customHook = function (data, cb) {
        if (data == 'get_online') {
            var srvSockets = io.sockets.sockets;
            // console.log(' wrker ' + cluster.worker.id + ' ' + Object.keys(srvSockets).length);
            cb(Object.keys(srvSockets).length);
        }
    };

And then somewhere , where you want to get online use :

                io.of('/').adapter.customRequest('get_online', function(err, replies){
                    var online = 0;
                    for (var i in replies){
                        online += (parseInt(replies[i]) || 0);
                    }
                    redisClient3.set('online', online);
                    STATISTICS.online = online;
                 });