Improving cost and efficiency by using AWS Lambda’s cache
In this blog, we’re going to discuss how global variables inside AWS Lambda work and how you can use them in your serverless applications to increase performance and cache values. Let’s first start with the definition of cache. According to AWS official documentation,
“In computing, a cache is a high-speed data storage layer that stores a subset of data, typically transient in nature, so that future requests for that data are served up faster than is possible by accessing the data’s primary storage location. Caching allows you to reuse previously retrieved or computed data efficiently”.
What are global variables in AWS Lambda?
A global variable in AWS Lambda is one that is defined outside of the scope of your main lambda handler function. It remains consistent between lambda invocations since it exists outside of the handler. To understand this, you must first understand how an AWS Lambda works in order to comprehend the global variable. When you initially receive a request for our lambda, AWS will spin up a container with all of the environment settings that you require for your lambda to download the code into that container, and then execute it. This is something which is also known as a cold-start of an AWS lambda. If you have variables declared outside of the main handler function, they are declared in a global space.
Once your first request has been completed, the lambda container will still exist for a certain period of time, so if you receive a second lambda request, that new request will execute in the same container and therefore you have access to that global variable and it’s contents that were declared.
Deep dive with a NodeJS Lambda example
Here is an example of an AWS Lambda function that is responsible for handling AWS DynamoDb requests. On each request, it will create a new connection with AWS DynamoDB and try to fetch the user information from the AWS DynamoDB table. This implies that the database connection will be re-instantiated for each lambda invocation.
Now let’s look at a different approach where we do not re-instantiated the AWS DynamoDB connection by applying Singleton Design Pattern for the AWS DynamoDB connection instance.
On the above modification did you notice something 🤔? Yes, you are right, we are actually fetching the same user with id = `dummy-user-id`. In this scenario, although we managed to limit the database connection we are actually making a database fetch call in each request when in all cases the value is the same. So let’s see what we can do about it, shall we?
Now, here we now make a cache global variable that will store the database result corresponding to the id and whenever a request comes it will try to find the user in the cache and thus return the result. If the user is not found in the cache, it will call the AWS DynamoDB to get the user and then put it in the cache and finally return the result. Here we can see that we have successfully optimized the number of AWS DynamoDB connections and calls per request along with minimizing the lambda compute time in total. This strategy can be greatly utilized where the data is consistent and can be cached inside lambda’s global space.
Some considerations you need to know about
Although the idea is great, it’s not ideal for all the use cases. In the above example, we have worked with data that is consistent in the database. But if the scenario is like the data is changing frequently then we need to think of a different approach. Now let’s talk about some lambda considerations as well, think about what will happen when there are numerous concurrent requests? You might think that all the requests will be handled inside the same container and the global variables will be persistent but that’s not the case here. AWS will spin up numerous containers operating at the same time to process those concurrent requests. Because there are distinct containers, they are isolated, which means that inside one of your containers, your global variable may be set to one value, but in another, it may be a different value. In this case, it’s a waste of resources to cache additional values when your database is most likely to be called in each container.
Another consideration is cache size and how much memory does the Lambda function require and how much memory is allocated? If the Lambda cache continues to expand, it may create memory exhaustion issues and prevent the function from functioning at all. These are some of the items to think about while constructing your AWS Lambda and utilizing these global variables.
Thank you for your time.