loading...
Lambda Store

Reuse Redis Connections in AWS Lambda

svenanderson profile image Sven Anderson ・1 min read
exports.handler = (event, context, callback) => {
    const redis = require('redis');
    const redis_client = redis.createClient({
        host: 'lyo474tg.lambda.store',
        password: 'hC3TECh!4Jui',
        port: 6379
    });
    redis_client.get("foo", function(err, reply) {
        client.quit();
        callback(null, reply);
    });
};

Any problem with the above code? It simply connects to a Redis database and returns an entry. But for each function, it opens/closes a connection. Yes, Redis connections are very lightweight but still if your use case is latency sensitive you may not like it. So let's move the client creation code to the outside of the handler and remove the client.quit().

const redis = require('redis');
const redis_client = redis.createClient({
    host: 'lyo474tg.lambda.store',
    password: 'hC3TECh!4Jui',
    port: 6379
});

exports.handler = (event, context, callback) => {
    redis_client.get("foo", function(err, reply) {
        callback(null, reply);
    });
};

Oops! The function times out. Why? Because AWS Lambda waits for the Node.js event loop to be empty. So the function does not return unless you close the Redis connection.

The solution: Setting context.callbackWaitsForEmptyEventLoop = false;, you tell AWS Lambda send the response right away when the callback executes, instead of waiting for the Node.js event loop.

const redis = require('redis');
const redis_client = redis.createClient({
    host: 'lyo474tg.lambda.store',
    password: 'hC3TECh!4Jui',
    port: 6379
});

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    redis_client.get("foo", function(err, reply) {
        callback(null, reply);
    });
};

Good, the above code works just as we expected.

Posted on by:

svenanderson profile

Sven Anderson

@svenanderson

Making the world serverless

Lambda Store

Lambda Store is the first the `serverless Redis` service. In this blog, Lambda Store engineering team shares their experiences on Cloud, AWS, Kubernetes, Redis and of course Lambda Store.

Discussion

markdown guide
 

How this code reuse connection when new lambda is triggered?

 

It does not, the client might think it is still alive because the process will be frozen after the callback has been invoked (source), but the server might already got rid of the client. There is no connection re-use. The library might reconnect (because it has some reconnect logic), but connection re-use is not possible.

 

I would recommend when using lambda one should probably use a database designed for serverless, such as serverless aurora or dynamodb. 🤔 Since lambdas can scale a lot when many concurrent requests occur the database connection limit could be reached quite fast.

I have used PostgreSQL with it before and a database pooler could help avoiding the limit, but would result in another non-serverless infrastructure.

 

Correct. So why the title of this article say so.

 

For new lambda, it definitely doesn't. But, a lot of time, you just reuse the hot lambdas. If this the case, i think it will reuse the connection from pool.

 

Are there any showcases/tests? How are the stale connections handled, how does the redis server react? For something like this it would be better to actually provide some more insight than just thinking something works 😅.

 

Lambda reuses the globally defined variables as long as the container of the lambda function is alive. But for cold start cases, yes it can not reuse the connection.

 

What happen with the client.quit? When hoy clóset the connection?

 

When you call client quit, the connection is closed and you can not re-use it. And there is no callback in the event pool for Lambda to wait for. So lambda function returns.

 

Yeah but then it will NEVER be closed, even when the Lambda is dropped, e.g. because it has been unused for days.

Lambda closes the connection when it expires the container (usually 15-20 minutes). However I understand your point. This is more like a hack. If you tolerate the latency of opening/closing the connection, it is cleaner to do it inside the handler.

What exactly happens to the connection when the container is frozen? How can it still be alive?