If you are familiar with the lambda execution environment and how it works for a single lambda function invocation, then it's a great opportunity for you to optimise your application's performance. The lambda execution environment enables you to persist and reuse DB connections, especially in situations where latency must be taken into account and connection open/close at the same time is not desirable.
👉 Lambda Execution Environment
Recently, I have implemented a solution for reusing Redis client connections across the invocations of a lambda function in a single lambda execution environment, I will share that here today.
Note that examples are given using TypeScript and
this blog could even help you to reuse your database connections (MySQL, PostgreSQL, etc.).
The code placed outside of function's main handler method is run only when execution environment is spinned up for the first time. Lambda keeps that code as long as environment is warm.
👉 Static Initialization
- Initialize a global variable outside the handler method that saves the Redis client object.
You can put this global Redis client variable in any file as per your function's code structure. Its not necessary to keep it in the handler method file.
Since, we want to reuse the database connections so, no need to end newly created connections.
- Hence, delete the connection-close logic from your lambda function. If you have multiple usecases in your lambda function then do it only for those where you want to reuse the existing connection.
Note that, once a lambda execution environemnt is taken down, these opened connections become useless and they can take up as much memory to cause OUT OF MEMORY errors in Redis/database server.
Configure an idle connection timeout in Redis.(or in your database service) to terminate these idle connections automatically.
You need to be careful here, analyse your application requirements and configure a timeout in Redis as per your need.
It shouldn't be larger than the lambda execution environment idle timeout which lies somewhere between 40-50mins. I found this stat online but there's no proper evidence about it. You may wanna perform tests to find it yourself :)
Also, it shouldn't be very small at the same time, otherwise, your lambda function might create new connections as frequently as it matches the previous rate and this whole effort becomes pointless.
You need to update the client creation logic based on two things:
- If existing client is active and working then function uses that instead of creating a new one.
- Client would be reset if existing connection is terminated by Redis/database engine in the wake of connection idle timeout.
There are multiple ways to manage it:
- Create a separate method called
getRedisClient()as shown in the below figure. Keep the client reuse/reset logic in that method and only use it throughout the lambda function for fetching an active Redis client. This approach is cleaner and maintainable. If your application can bear the extra
ping()call, then it is a good solution.
- Here, move the client reset logic into the API methods like below. It saves you the
ping()call, so it is more performant. This approach is favorable when your lambda function is doing only one task. If you have multiple business logic operations in your function, then maybe not a good option. You will have to insert the client reset logic in all those methods where Redis operations are performed. Not good for clean code principles.
Implement a Global Error Handler in the lambda function or a pattern like that. See if lambda retry mechanism helps for that. Basiaclly, idea is to transmit all errors to that error handler where the Redis/database client is reset as done in the second approach.
This looks like a perfect solution that avoids the extra
ping()call in the first approach and the repetition pattern in the second approach.
I chose the first approach as it suited my application. The third option is just an idea that I didn't experiment with cz I am busy with other things🥲. Maybe you can give it a try and let me know :)
After updating the Redis client creation logic, your lambda funtions are all set🚀 to reuse the existing connections.