Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing the return value of node.js setTimeout in redis

I'm using setTimeout in Node.js and it seems to behave differently from client-side setTimeout in that it returns an object instead of a number. I want to store this in redis, but since redis only stores strings, I need to convert the object to a string. However, using JSON.stringify throws a circular reference error. How can I store this object in redis if I want to be able to fetch it from redis and call clearTimeout on it?

like image 862
user730569 Avatar asked Jul 02 '12 20:07

user730569


3 Answers

In the newer versions of node, you can use the Id of the Timeout object instead of the object itself to end the loop.

   redisClient.set('time', JSON.stringify(10))
   let timeoutObject = setInterval(async function(){
      let time = await JSON.parse(redisClient.get('time'))
      if(time === 0){
       let intervalId = await JSON.parse(redisClient.get('intervalId'))
       clearInterval(intervalId)
      }
       time -= 1
       redisClient.set('time', JSON.stringify(time))
    }, 1000)
    
    let intervalId = timeoutObject[Symbol.toPrimitive]()
    redisClient.set('intervalId', JSON.stringify(intervalId))

This is just an example of a timer built with setInterval and redis combined. As you can see, you can grab the Id of the Timeout Object and store that to end setInterval's execution instead of trying to store the whole object.

Here is the link to the node docs: https://nodejs.org/api/timers.html#timers_timeout_symbol_toprimitive

like image 144
Goose9192 Avatar answered Nov 15 '22 08:11

Goose9192


You cannot store the object in Redis. The setTimeout method returns a Handler (object reference).

One idea would be to create your own associative array in memory, and store the index in Redis. For example:

var nextTimerIndex = 0;
var timerMap = {};

var timer = setTimeout(function(timerIndex) {
    console.log('Ding!');

    // Free timer reference!
    delete timerMap[timerIndex];
}, 5 * 1000, nextTimerIndex);

// Store index in Redis...

// Then, store the timer object for later reference
timerMap[nextTimerIndex++] = timer;

// ...
// To clear the timeout
clearTimeout(timerMap[myTimerIndex]);
like image 45
legege Avatar answered Nov 15 '22 09:11

legege


I was attempting to do the same thing as the OP. My solution was to set the timeout with a conditional check on a new key inside the timeout in my disconnect handler:

redis.hset("userDisconnecting:" + userId, "disconnect", 1);

setTimeout(function() {
    redis.hget("userDisconnecting:" + userId, "disconnect",
     function(err, result) {
        if (result.toString() === "1") {
           //do stuff, like notify other clients of the disconnect.
        }
    });
}, 10000);

Then, when the client connects again, I set that key to 0, so the stuff that needs to fire on true disconnect doesn't happen:

redis.hset("userDisconnecting:" + userId, "disconnect", 0);

The timeouts themselves aren't persistent across server restarts, but you could solve that by kicking off a sweeper method on startup. Connected clients would come back "online" pretty quickly.

like image 1
Mike Atlas Avatar answered Nov 15 '22 08:11

Mike Atlas