Ioredis: Unref new connections

Created on 25 May 2015  Â·  8Comments  Â·  Source: luin/ioredis

Node process stays live as long as there are open connections, so when running a short-lived process, the Redis connection prevents the process from exiting when there's no more work to be done.

Right now we use this:

  client.on('connect', function() {
    client.connector.stream.unref();
  });

A proper API, connection option, or default behavior would help here.

enhancement wontfix

Most helpful comment

I could definitely use this feature. unrefWhenDrain would also be fitting for my use-case.

All 8 comments

Yes, it would be useful to have a method like client.unref() to do the same thing since client.connector.stream isn't a public variable. I'm going to add this feature in next release.

Thanks

After some tests, unref seems to be a little unpredictable when used to exit the process when there's no more work to be done. For example:

var Redis = require('ioredis');
var redis = new Redis();
redis.on('ready', function () {
  redis.get('foo', console.log);
  redis.stream.unref();
});

The above code will exit without waiting the result of get command being printed. However, the following code will have the result printed before the process exits.

var redis = new Redis();
setTimeout(function () {
  redis.get('foo', console.log);
  redis.stream.unref();
}, 100);

What's more, since unref doesn't have any awareness of the Redis protocol, when invoking some blocked commands(e.g. brpop), process will exit without waiting the block command being returned.

I'm considering add an option unrefWhenDrain or something similar. When the option is enabled, ioredis would call unref when there's no more pending command and call ref when a new command is invoked.

'unref' is not protocol aware, it can't infer intent. It's up to the application developer to use it correctly.

If you're running an HTTP server, you want to keep it alive while the socket is open, so you're not going to unref. If you have a socket for logging, you don't want that socket to keep the process from exiting, so you're going to unref it.

Redis, like raw sockets, supports many different use cases. If you're using it for storage/cache, primarily read/write operations, then you probably want to unref. If you're using Redis for queue processing, or listening to pub/sub messages, you don't want to unref.

So this could be a connection open or API call on the connection, and let the application developer decide when to use this option based on use case. Or it could be handled by ioredis depending on which command is currently executing (e.g. unref for get and ref for brpop).


There's a different issue with get. From looking at the code, it looks like every command that executes will block until something happens on the socket. If the Redis server goes away (crash, network issue) nothing happens on the socket, and the command will hang forever.

A server that shuts down will send a FIN packet which tells Node to close the connection. But if the server goes away there's no chance of that happening. TCP can handle that by detecting no activity on the socket — the server can be configured to always send something (keep-alive), and the client has a timeout — but that seems to be turned off.

But even when used, socket timeouts are not good enough. You want the timeout long enough so it doesn't disconnect during periods of inactivity, but you don't clients waiting forever, so you need a quick way to detect crashed servers, by having a shorter timeout on the command.

Commonly this is done with setTimeout, so you have a timer running while executing a command and waiting for it to complete, which keeps the process running until the timeout is cleared (command completed, successfully or not). So that would fix the problem with get.

brpop is a bit more tricky, because you want it to wait, possibly forever, but only if the server is alive. So one option is to let TCP detect a crashed server using keep-alive and timeout, but I'm not sure if Redis supports that. The other option is to use timeouts, then do some noop to see if Redis still responding, and then repeat the command.

That mechanism will likely also use setTimeout, so if you have a way of dealing with crashed servers that uses timeouts, you can unref the underlying socket.

I could definitely use this feature. unrefWhenDrain would also be fitting for my use-case.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 7 days if no further activity occurs, but feel free to re-open a closed issue if needed.

@luin Could we reopen this? I need unrefWhenDrain for AWS Lambda usage.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 7 days if no further activity occurs, but feel free to re-open a closed issue if needed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AdriVanHoudt picture AdriVanHoudt  Â·  4Comments

luckyscript picture luckyscript  Â·  5Comments

dfee picture dfee  Â·  4Comments

haoxins picture haoxins  Â·  5Comments

PhillipWinder picture PhillipWinder  Â·  3Comments