Bug Report: Python Lamba Environment Failure of Isolation?

0

I implemented the singleton design pattern in a python 3.6 lambda and found that a previous lambda execution impacted the current execution. Specifically, the instance of the singleton class was the same across multiple lambda environments. Im curious what these means for how python lambdas are isolated?

I followed the singleton implementation described in 'Method 3' of this popular stack overflow post: https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python

Here is an example.

Lamba code:

import json

class Singleton(type):
_instances = {}

def __call__(cls, *args, **kwargs):  
    if cls not in cls._instances:  
        cls._instances\[cls] = super(Singleton, cls).__call__(*args, **kwargs)  
    return cls._instances\[cls]  
  

class Foo(metaclass=Singleton):
def init(self):
self.bar = []
print(f"'bar' attribute is {self.bar}")

def lambda_handler(event, context):

foo = Foo()  
  
print(f"after instantiating 'foo', bar attribute is {foo.bar}")  
foo.bar.append(0)  
print(f"after append, bar attribute is {foo.bar}")  

Resulting Cloudwatch Logs after manually triggering the lamba several times in a row. I believe a new instance is not being instantiated in lambdas beyond the first as the 'bar' attribute contains data from the previous lambda.


01:57:14 START RequestId: 69e78a31-4a5e-4e84-abd9-44108f390838 Version: $LATEST
01:57:14 'bar' attribute is \[]
01:57:14 after instantiating 'foo', bar attribute is \[]
01:57:14 after append, bar attribute is [0]
01:57:14 END RequestId: 69e78a31-4a5e-4e84-abd9-44108f390838
01:57:14 REPORT RequestId: 69e78a31-4a5e-4e84-abd9-44108f390838 Duration: 1.49 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 56 MB Init Duration: 114.96 ms
01:57:21 START RequestId: adacf02b-39b8-468e-a7a5-f4c09bda5758 Version: $LATEST
01:57:21 after instantiating 'foo', bar attribute is \[0]
01:57:21 after append, bar attribute is \[0, 0]
01:57:21 END RequestId: adacf02b-39b8-468e-a7a5-f4c09bda5758
01:57:21 REPORT RequestId: adacf02b-39b8-468e-a7a5-f4c09bda5758 Duration: 1.38 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 56 MB
01:57:22 START RequestId: d66468cd-f09d-4c28-9343-0eeb52b316e9 Version: $LATEST
01:57:22 after instantiating 'foo', bar attribute is \[0, 0]
01:57:22 after append, bar attribute is \[0, 0, 0]
01:57:22 END RequestId: d66468cd-f09d-4c28-9343-0eeb52b316e9
01:57:22 REPORT RequestId: d66468cd-f09d-4c28-9343-0eeb52b316e9 Duration: 1.62 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 56 MB
01:57:23 START RequestId: 89813b23-9cb1-4284-ab11-472b84151666 Version: $LATEST
01:57:23 after instantiating 'foo', bar attribute is \[0, 0, 0]
01:57:23 after append, bar attribute is \[0, 0, 0, 0]
01:57:23 END RequestId: 89813b23-9cb1-4284-ab11-472b84151666
01:57:23 REPORT RequestId: 89813b23-9cb1-4284-ab11-472b84151666 Duration: 1.39 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 56 MB

Edited by: lookatthisgraph on Jan 20, 2020 8:44 PM

Edited by: lookatthisgraph on Jan 20, 2020 8:44 PM

Edited by: lookatthisgraph on Jan 20, 2020 8:44 PM

Edited by: lookatthisgraph on Jan 20, 2020 8:46 PM

asked 4 years ago256 views
1 Answer
0
Accepted Answer

Hi Lookatthisgraph,

It looks like you are experimenting with Lambda runtime environments!

== A Short Answer And Why It Is Not A Bug But A Feature ==
Well, according to the mighty internet:

Possibly the simplest design pattern is the singleton, which is a way to provide one and only one object of a particular type...
https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html

And based on the fact you have created it outside of the handler, and also according to the following article (it's using node as an example but this behaviour is same across different runtimes):

The first time a function executes after being created or having its code or resource configuration updated, a new container with the appropriate resources will be created to execute it, and the code for the function will be loaded into the container. In nodejs, initialization code is executed once per container creation, before the handler is called for the first time.
https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/

So this is the normal behaviour from the Lambda container, and in fact, many of our customer is utilizing this feature actively.

== How Can I Use This Feature ==
So, the takeaway from the best-practices:

Take advantage of execution context reuse to improve the performance of your function. Initialize SDK clients and database connections outside of the function handler, and cache static assets locally in the /tmp directory. Subsequent invocations processed by the same instance of your function can reuse these resources. This saves execution time and cost.
To avoid potential data leaks across invocations, don’t use the execution context to store user data, events, or other information with security implications. If your function relies on a mutable state that can’t be stored in memory within the handler, consider creating a separate function or separate versions of a function for each user.
https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html

Hope this answers your question.

AWS
answered 4 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions