In a previous blog post I described the steps for implementing Lambda functions in Java. While working on the implementation of the simple microservice presented in that blog post I ran into some unexpected obstacles compared to my previous experiences working with other runtimes for AWS Lambda. In this blog post I want to share what I’ve learned.
Of course, all these things are only a hassle when you use it for the first time or come back to it after a few years using something else like I did.
What really came as a surprise was how different the Java runtime is when you look at its resource consumption. Of course, using a full-fledged IDE on your machine and needing to actually compile the source code means that you need a machine with a decent amount of memory and CPU. Most laptops and desktops should provide enough power nowadays to fulfill these needs.
On the other hand, I did not expect a big impact on the execution environment.
First, the deployment package weights in at a few megabytes, even for something so simple as the microservice in the example mentioned above. In general, this is no real problem. But you will hit the initial limits imposed by AWS for uploading deployment packages to Lambda much sooner.
The thing that really surprised me was the impact on the resource consumption for a function itself. The simple functions I’ve created can not be run with the lowest memory configuration of 128MB available for Lambda functions in AWS. You actually need at least 192MB. Since the costs for running Lambda functions is dependent on the amount of memory it uses this means that anything you run with Java is essentially more expensive than doing the same thing with a runtime like Node.js or Ruby.
When I was ready to test an end-to-end use case - getting invoked by the API Gateway, reading data from DynamoDB and returning it to the caller - for the first time I was surprised by the cold start time that was involved in this simple use case. It took approximately 20 seconds to run this Lambda function when it was invoked for the first time. Of course, after that it only took around 100ms to execute. I was not used to such a long cold start time from the other runtimes I’ve experience with.
This might not seem to be a big problem because it might suggest that it only happens for the first invocation of a function and there might be solutions for working around that. But since Lambda scale automatically under load the actual behavior can not be easily predicted and therefore workarounds become much harder.
Looking back at my experience with using Java for implementing Lambda functions I came to the conclusion that one needs to carefully think about the use cases for using Java with AWS Lambda. Basically, at the moment it only makes sense to use Java when the impact on the cold start time can be neglected or when there are methods in place to circumvent it.
Additionally, I would only recommend it to people who are already pretty familiar with Java programming in general. I think it does not really make sense for someone to learn Java programming when the goal is to build serverless applications on AWS.