
How to avoid AWS lambda cold start
In this short article we are going to talk a little bit about how you can avoid the cold starts of lambdas in AWS.
Reasoning
Lambda cold starts can be tricky. Assuming you have a system that uses lambdas to do its job, there might be cases where the actual lambda run takes under 1 second but the cold start takes up to 5 seconds, meaning that instead of getting your feedback from the system in about 1 second, you get it in approximately 6 seconds which sometimes it might be a little annoying, especially for users that use an UI to interact with your system.
Presuming I press a button in your app that should give me information about a specific entity, I would expect it to take about 1 second (and it might if the lambda is warm), sometimes it takes up to 6 seconds, which is a long time for a real-time system to respond.
Assumptions
- code is written in python
- you have basic knowledge of AWS Lambda, AWS APIGateway
- basic REST API knowledge
Prerequisites
So let’s assume that we have a system that uses APIGateway to create an API and for each endpoint of that API, you have a lambda function that responds to incoming requests. We are going to assume that our API has only 3 endpoints, and they’re all related to the same entity, and since I like dogs, we’re going to be getting, adding or deleting dogs from a database. Here’s a diagram to showcase that.

So our API looks like this:
- GET /dogs/{dog_id} → so you can get a dog from our database base on its entity ID
- POST /dogs → so you can add a dog to our database
- DELETE /dogs/{dog_id} → so you can delete a dog from our database, based on its id
As you can imagine, the code from the lambdas will be pretty much similar, especially if we use an ORM (object relational mapper) to work with the database.
Here’s how getting a dog would look like, keeping in mind that I am presuming you have a basic idea of how a lambda works, how the event in a lambda looks like and I am not dealing with error handling in this piece of code.
def handler(event, context):
dog_id = event.get("dog_id")
# Considering that Dog is an ORM model
# And that a get on it returns a dictionary like object
dog_object = Dog.get(dog_id)
return dog_object
Similarly, for the addition of a dog, we would have some information in the event that is incoming such as color, age, weight, etc., and we will insert that into our database. For the deletion of a dog, the code is almost identical with us simply using the delete method instead of get from the ORM model.
Solutions
Keeping the lambda warm
One solution that I have seen applied is to “keep the lambda warm” which basically means that from time to time (usually it’s 15minutes, the maximum amount of time that a lambda can run), you call that lambda such that the container that the lambda is stored in and is running is also kept alive and the resource do not get de-allocated.
This of course come with its drawbacks, since you will be doing a lambda call once every 15 minutes at least, which leads to at least 96 lambda calls extra per day for you for just one lambda, because in a 24h day, you would have 4 lambda calls every hour. This will inevitably lead to more cost, especially if you also have attached to your lambdas other AWS services such as lambda insights, which tends to increase the costs quite a bit with more lambda calls.
Alternatively, you could do something in-between, maybe call the lambda every hour, reducing the number of extra calls to just 24 in a day.
Again, I have seen systems that use this approach, but I am not saying you should do it, in the end it all depends on the way your system is built.
Interactors
One approach which I have seen working and I feel more comfortable in choosing is to have an interactor
An interactor is a class, or set of functions, that takes in the incoming event and decides what to do with it within a lambda.
As an example, for our system, we would create an interactor for the 3 endpoints, and we will have only one lambda serving all 3 endpoints. The interactor code should look a little something like this:
class Interactor:
def __init__(self, event):
self.event = event
def get_dog(self):
# Code to get the dog from the database and return it
pass
def create_dog(self):
# Code to create the dog and add it to the database
pass
def delete_dog(self):
# Code to delete the dog from the database
pass
def run(self):
# We are assuming that we have an integration within our infrastructure
# that is passing the HTTP method in the event
if self.event.get("httpMethod") == "GET":
self.get_dog()
elif self.event.get("httpMethod") == "POST":
self.create_dog()
elif self.event.get("httpMethod") == "DELETE":
self.delete_dog()
def handler(event, context):
# We create the interactor entity
interactor = Interactor(event=event)
# we let it run, choosing by itself what needs to be done depending
# on the incoming event
interactor.run()
What this will do for us is that now, we will not have 3 lambdas responding to 3 different types of requests, but only 1 lambda responding to 3 types of requests, which means that in theory, it will be kept warm by itself (of course, there might be cases when it will still need the cold start), but all the calls that are done are calls that NEED to happen for the system to do its job, and not calls that are just supposed to keep the lambda warm.
Now your infrastructure should look a little something like this:

Now, there might be a case when an interactor might become way more complex than you wish it to become, and that is one of the drawbacks of using this approach. But you could come up with better splits of the logic to have instead of 10 lambdas, just 2 with interactors, so the solution can be changed a bit to suit your needs.
Conclusion
There might be other solutions to this, some of them even more elegant and maybe easier to implement, but these two I have seen used quite a lot in live system and they do work, provided that you take into account their drawbacks.