API Gateway with REST endpoints as Lambda functions: how to get singleton like behavior ?

0

Hello, I have an API Gateway exposing a Java REST API. Each endpoint is integrated with a lambda function. There are several separate lambda functions, each one having its own handler. They are extending the same base class. The base class defines a static Hashmap inherited by each lambda function. One lambda function stores data in this Hashmap and another one is supposed to get the data. Each Lambda function is triggered by a separate HTTP request.

This works as expected when deployed in a CDI container as a singleton. But, when deployed to AWS, with the API Gateway, the lambda function supposed to get the data stored by the other one finds the hashmap empty. Meaning that, despite of being static, the hashmap is instantiated with any HTTP request.

This is equivalent to the CDI @RequestScoped annotation. Is there any way to configure the API Gateway endpoints such that to have the same effect like when using CDI @Singleton or @ApplicationScoped annotation ? Many thanks in advance. Nicolas

profile picture
Nicolas
asked a year ago339 views
6 Answers
0
Accepted Answer

"What you need to understand is that Each lambda function, and even each function instance run in their own VM". This is simple to understand and it doesn't require additional explanations. And what I'm trying to explain is that, having several Java sub-classes, which extend the same static superclass, is like having a single class instance. This is because the static properties, also called class properties, aren't inherited and stay with the superclass instance. Consequently, having several Lambda functions, mapped to this Java model hierarchy, should be like having a single Lambda function. In this context, calling different Lambda functions shoud behave exactly like calling the same instance of the same Lambda function. This is because these Lambda functions are mapped to Java classes which are subclasses of the same static superclass. But, as you're saying, things don't happen this way and, hence, AWS Lambda don't respect the Java basic inheritance and instatiation rules. Running the same Java classes as such or as Lambda functions don't provide the same results, which isn't consistent. This is all what I wanted to mention and now I'll close this ticket.

profile picture
Nicolas
answered a year ago
  • If you create a Java application that uses some class with static members and you run the same process twice, even on the same host, the two processes will not share the same static members. Each process has its own memory space and as such they don't share static members. Exactly the same happens in Lambda. Each Lambda function runs in its own process on its own micro VM.

0

Hi,

Many thanks for your reply. While I understand what you're saying, I can't agree. My understanding is that a static block in Java should have the same behavior, should it be executed in a Lambda function or in any other runtime. It wouldn't be at all consistent that the same Java code would have a different result, depending on what kind of runtime it is executed.

In my case, I have a class hierarchy as shown below:

diagram

In this class hierarchy, the superclass class defines a static HashMap in which the subclasses are putting and getting data. For example, the superclass contains a static block similar to the following:

   static {
     if (myMap == null)  {
       myMap = new MyMap();
       log.info("### MoneyTransferOrder.handleRequest(): myMap was null, instantiated {}", myMap);
     }
     else {
       log.info("### MoneyTransferOrder.handleRequest(): myMap was not null {}", myMap);
     }
   }

The static block above is supposed to be executed only once, the first time that one of the subclasses is instantiated. Hence, during the first instantiation the message "myMap was null, instantiated" should be displayed while during all the other subclasses instatiations the message "myMap was not null" should appear. This is exactly what happens when running this code in a local JVM.

However, running this same code in AWS, implementing classes as Lambda functions, systematically displays "myMap was null, instatiated". Hence every Lambda function creates a new instance of the HashMap. I fully agree with you when you're saying that the data should be kept in a database or in a cache but, as long as using a HashMap works as expected in a local JVM, it isn't acceptable that it has a different behavior in AWS.

Hence, I think that the API Gateway should have a way to say that the exposed API has a request, session or application scope, as this is the case of any REST endpoint. However, I didn't find any such functionality.

Kind regards,

Nicolas

profile picture
Nicolas
answered a year ago
  • If you run your Java application, with a static block, on two different servers, do you still expect it to be the same memory? It is not. Same is for Lambda functions. Each Lambda function instance runs in its own micro VM, so it is not sharing any memory with any other instance. Your singleton is a singleton, but in the context of that VM. Each VM will have its own singleton.

0

My understanding is that, in Java Lambda, a static initialization block will be called when the function runs for the first time in a new container, and each subsequent Lambda execution that is sent to that container will be able to skip the initialization step and, hence, to find an already initialized state. Is that correct ?

profile picture
Nicolas
answered a year ago
  • You are correct in your understanding. As you said, in the SAME container (not really container, but never mind). If we need to create a new one, it will create a new static map, which is not shared with the other execution environment.

0

@Uri: "If you run your Java application, with a static block, on two different servers, do you still expect it to be the same memory?" Not sure what exactly you mean by "to be the same memory" but, if you mean "to share the static HashMap", then the answer is no, I don't expect that, as you probably don't neither. However, if I run a Java application on a single server and this Java application consists in calling several times the same class, in which a static HashMap is initialized, then I expect that this static HashMap keeps status between several calls. My case is similar, in the sense that I'm running several Lambda function, implemented as separated Java classes which are subclasses of the same base class, but the static HashMap is in the base class. Given that statics aren't inherited, invoking several Lambda functions as subclasses of the same class is like invoking the base class several times. And invoking the base class several times means initializing the static HashMap upon the first call and reusing the status saved in it upon the subsequent calls. Not sure what is you level of Java and how well are you hadnling these details, but this is what any Java developer would expect while deploying a class hierarchy, whatever the runtime is, be it in the cloud or not.

profile picture
Nicolas
answered a year ago
  • I am proficient with Java and even more, with Lambda (I am a serverless specialist solutions architect with AWS). What you need to understand is that Each lambda function, and even each function instance, run in their own VM, which is just like running them on different hosts. Because of that, each instance and each function, has their own copy of the static HashMap.

    You can reuse that map between invocations in the same instance, but not between instance. Just to explain, when the Lambda service receives an invocation request for a specific function, it first checks if there is a VM with that function, which is in idle state, i.e., not handling a request at the moment. If there is such VM, we let it handle the new request (and in this case it will have access to whatever information you saved in the static map in previous invocation in the SAME VM). If there is no idle VM, we create a new one (this is what we call cold start). In this case we allocate new memory, load the code and so on.

0

This is how Lambda was designed, it has nothing to do with API Gateway or any other service that invokes the functions. Each Lambda instance runs in its own micro VM, with nothing shared between instances. Not only instances of different functions, but also instances of the same function. For this reason we say that Lambda functions are stateless.

What you need to do is save the information outside your Lambda function. It can be in DynamoDB or maybe also ElastiCach for Redis or Memcachd. In this case you can have many instances of the same and different functions accessing the same table/cache.

profile pictureAWS
EXPERT
Uri
answered a year ago
0

I have inserted a PNG file showing a class diagram but, for some reasons, it doesn't appear in the post above.

profile picture
Nicolas
answered a year 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