Lambda calls share their runtime and it’s super scary — Here is why
By: Santhosh Gandhe
Tags: Lambda, AWS, AWS Lambda, Functional Programming, Aws Cloud

AWS Lambda functions are very popular in the developer world right now. While these are a popular choice, a lot of developers still struggle to understand their execution behavior when it comes to runtime sharing and state sharing. If you ask a somewhat fundamental but critical question like “do two different lambda invocations share their runtime or any common state between them?” you will probably get a “NO” answer. AWS documentation clearly explains that they DO share some common state such as SDK clients, database connections, and other static resources But it won’t give any insights into sharing the runtime part. This sharing concept often creates pretty scary issues with the developer’s own static code in the lambda. Check out the simple python example below.

very simple lambda code with a static array

To add more confusion, the output is not always consistent like it is shown above because AWS creates new instances of your function code as needed depending on many other constraints: idle time, number of concurrent requests, provision concurrency set, etc. Each new instance starts with a clean slate of the static objects but the requests that are hitting the previously spawned function instances will continue to share that static object’s state.

There are multiple reasons why developers fall into the trap of assuming that each lambda call is fully isolated and there is nothing shared between multiple lambda calls.

We call these serverless functions. In the functional programming paradigm, a function is a piece of code that is called by a name, takes some input arguments given, and works through the logic to optionally return its final result. More importantly, the return value of the function is expected to be consistent for given input arguments. In this case, it is not. Basically, if you are assuming that each invocation of a lambda function is like the below piece of code then you are wrong.

# This is not how each lamdba call invoke your code



# first call

➜ ~ python3

>>> from test_static_loading import lambda_handler

>>> lamda_handler({"counter": 1}, context)



➜ ~ python3

# second call

>>> from test_static_loading import lambda_handler

>>> lambda_handler({"counter": 2}, context)

If this is not how your lambda code is invoked then how AWS is invoking your lambda code? Well, I exactly don’t know how AWS is invoking but here is what I think it is doing.

# It is more like below

➜ ~ python3

>>> from test_static_loading import lambda_handler

>>> # first call

>>> lamda_handler({"counter": 1}, context)

>>> # second call

>>> lamda_handler({"counter": 2}, context)

If you see in the above code snippet, the module is imported once and used to invoke multiple times and that is how multiple lambda calls are sharing their runtime and static object state.

Going through the AWS documentation on the lambda execution environment lifecycle, we understand why it behaves the way it does. There are three main phases of the execution lifecycle as described in the above-linked AWS documentation page.

Init. This phase is executed once per instance of the lambda function. It includes initialization of extensions, bootstrapping of the runtime, and running of the function’s static code Invoke. This phase runs for each lambda invocation Shutdown. This phase is invoked once for each instance shutdown action

The static resources that are expected to be shared between the lambda calls of a particular instance of lambda code are like database connections and SDK clients etc which are purely static, sharable, and expensive to initialize so they are worth sharing between the calls but this design is not obvious enough to the developers. Also, the more recent features released by AWS, such as RDS proxy and various lambda invocation hooks are taking away the need for initializing these expensive static resources within the lambda code. So what is now left is just creating confusion for developers with their static objects.

In conclusion, Avoid using static objects holding any state-specific to a particular request. As long as the state is sharable maybe like a logger object or an S3 client connection or any other SDK client objects, it is ok to use them as a static resource. Here are some additional resources to read more about how lambda runtime works under the hood.