When it comes to software engineering it’s generally best to use the correct tool for the job, but of course we all have personal preferences. Any collection of Developers (company, team, individual et al.) should be striving to use the best available technologies, however, they often have experience and predilections that may contradict or subvert that mindset. You may be aware that X is better than Y at solving the problem you are facing, but if you can get a solution together using Y in a fraction of the time because you know it inside out, technology Y will definitely be tempting.
With that in mind I’d like to talk about Java being used in a ‘function as a service’ cloud solution; in this example AWS Lambda. I’ve put some effort into researching some of the major available languages available for Lambda, the drawbacks of Java (primarily cold starts), and how to overcome them.
What Are Cold Starts?
I was recently part of a team who implemented a serverless solution using AWS Lambda, and with little discussion it was decided that we would use Python. However, after the project had started one of my peers, who I’m going to call ‘Ella’, started asking, “Why did we have to use Python and not Java?”. My response at the time was an uninformed, “Doesn’t Java have some issues when running on Lambda”, to which I was swiftly told that “Java is a perfectly functional AWS Lambda language”. It was the next week before Ella remembered that the Java Lambda she had previously built, timed out every Monday morning and needed to be pinged ahead of time to work properly. This is exactly the Java cold start problem.
What’s going on here is Amazon managing its resources with regards to the Lambda. Your Lambdas are not magic, they in fact run on Firecracker containers. AWS has a number of these ready to go and if you get a lot of requests then it will increase this number. However, if you are getting no requests for an extended period, then they will scale down to zero. Then when your Lambda gets called it has to create a new container for you and start up Java before it can respond. For interpreted languages like Python, this is a small task, but for compiled languages like Java, it takes a bit of time.
A Bit of Code to Prove the Point
I’ve put together some code to illustrate this problem. The high level concept is easy; write a simple hello world Lambda, let a weekend pass, then run it and figure out how long it takes. Easy, right? Well yes, that would be fine if I was a sane man, but I wanted to go further than that. Firstly, I wanted to compare the speed of Java to a few of the more common Lambda languages (Golang, Node and Python). This was tricky because I had never even looked at Golang code before, but I did it anyway. Then I thought it would be nice to have the exact number of milliseconds it takes. So I decided to wrap my Lambdas in AWS API Gateway restful service, which can capture the time the call was made. Then the code in my Lambdas can just do some basic maths. Finally, I wanted to script it all so you can do the same experiment too.
Here are some code snippets, if you want to skip my GitHub repo completely.