KKK
KKK

Reputation: 1085

How to delay and consolidate pub/sub with Redis/Node/Rails

I got a RubyOnRails application that uses a Node.js/Socket.io server to push out trading information to all clients connected. Whenever a trade is executed the client screens get updated with the information of the last trade.

As the trading frequency increases, it gets rather annoying that the update happens every second or even more frequent. I am looking for a way to e.g. push the updates to the clients only max. once every 5 seconds, i.e. if no trade happens then nothing is pushed.

What I have so far is: I push the trading info to Redis from the Rails application via:

REDIS.publish('tradeupdate', ..... )

and the Node server does something like:

cli_sub.subscribe("tradeupdate");
cli_sub.on("message",function(channel,message) {
    io.sockets.emit('ablv', message);
});

and the client then does

socket.on('ablv', function (data) {
    obj = JSON.parse(data);
    .....
});

The aim is to have only the last message within a given period (e.g. 5 seconds) to be send out from Rails to Node or from Node to clients.

Upvotes: 2

Views: 2097

Answers (2)

Hari Gopal
Hari Gopal

Reputation: 718

What's stopping you from buffering the messages, and using a simple timer to perform the emit once every five seconds?

var last_message = null;

cli_sub.subscribe("tradeupdate");
cli_sub.on("message",function(channel,message) {
    last_message = message;
});

setInterval(function() {
    io.sockets.emit('ablv', last_message);
}, 5000);

Upvotes: 2

arghbleargh
arghbleargh

Reputation: 3160

It looks like what you need here is a helper for throttling your function calls. For example:

var makeThrottler = function() {
    var toRun = null, timeout = null;

    function doRun() {
        if (!toRun) {
            // nothing to run; we set timeout to null so that the
            // next function to execute knows to run immediately
            timeout = null;
            return;
        }

        // set a timeout of 5s before
        timeout = setTimeout(function() {
            doRun();
        }, 5000);

        // we need to do this temp thing to protect against
        // calling executeThrottled again within toRun
        var temp = toRun;
        toRun = null;
        temp();
    }

    function executeThrottled(fn) {
        // this is the function we want to execute next; it
        // overwrites any function we've stored earlier
        toRun = fn;

        // if we already ran within the last 5 seconds, don't do
        // anything now (our function will be called later)
        if (timeout)
            return;

        // execute the function right away
        doRun();
    }

    return executeThrottled;
}

And here's an example of how to use it:

var throttled = makeThrottler(), x = 0;
function increment() {
    throttled(function() {
        console.log(x);
    });
    x++;
    setTimeout(increment, 1000);
}
increment();

The increment function increases x by one every second. The logging is throttled, so the output you will see is 0, 5, 10, etc. (They might be occasionally off by one because of slight timing inaccuracies.)

Your original code would become something like:

cli_sub.subscribe("tradeupdate");
cli_sub.on("message",function(channel,message) {
    throttled(function() {
        io.sockets.emit('ablv', message);
    });
});

Upvotes: 1

Related Questions