Study notes for AWS Lambda.
Runtimes and Performance
Lambda uses runtimes to invoke your code. Even if you are writing your handler in a natively compiled language such as C/C++, Go, or Rust, you will still need at least an OS Runtime. Other non-natively compiled languages are provided a managed runtime to execute in. These managed runtimes are updated and patched by Amazon, which relieves you from the labor of keeping your servers up to date. If you don’t like that, you can change the update behavior of your Lambda functions. You can also upload container images to ECR to use for the runtime as well, and Lambda will actually pre-emptively pull the container image before the function is invoked and will hold it “for multiple weeks”, which is quite nice and I wish EKS and ECS would also natively cache container images.
However, I think it is best to be careful when choosing a runtime for your Lambda function. Although Node, Python, and Java may be comfortable and quick to code in, they are also massively inefficient compared to Go and Rust. Because Lambda is charged per millisecond of execution time, you can save a good amount of cash by selecting a runtime with less overhead.
For example, Xebia performed some benchmarks of popular runtimes and found that Scala costs 30 times more than Rust! You don’t have to use Rust, but other runtimes such as Go and C/C++ show similar performance to Rust as well.
Another company, Scanner also performed some benchmarks and had similar results: Rust was out front, closely followed by Go, and Java and Python trailing far behind. One interesting note from their benchmarks (which I highly recommend you read) was that the JSON parser included in the Go standard library was exceptionally slow. So slow that using it resulted in execution times higher than the Python test! They switched the JSON parser to valyala/fastjson, which resulted in execution times closer to Rust.
maxday also created this website that allows you to look at Lambda execution times updated daily. He also seems to have discovered that the Lambda runtime API is written in Go, interesting.
DB Connection Pooling
When your Lambda function is invoked, it must connect to all the databases and resources needed to complete its task.
Starting connections can take some time, which will result in a large amount of overhead. If possible, try to use connection pooling proxies to keep a collection of connections ready for when your Lambda needs them. For example, Amazon RDS has an Amazon RDS Proxy that can manage a pool of DB connections to use with Lambda. This way, your Lambda function can connect to the proxy and have a DB connection immediately.
Do you actually need Lambda?
Before you hop on the serverless Lambda bandwagon, take a moment to consider this: Lambda can be expensive.
Lambda is another layer of abstraction that AWS provides. Lambda abstracts away the OS and potentially the HTTP API framework that your code runs inside. In exchange for this abstraction, AWS charges you more money per request.
Notice that the pricing is per request while other compute options such as ECS, EKS, and EC2 are per second.
Therefore if your Lambda is being invoked a few times per day, then Lambda is your best option. But if your Lambda is being invoked millions of times per day, then you should really look into using something like ECS or EC2 with autoscaling.
This is why “spiky” traffic is good for Lambda, while constant traffic is good for ECS/EKS/EC2. If you have historical data available, then you should use that historical data to determine which type of compute is best.
Recommended Reading
- cardgames.io’s attempted switch to Lambda that was too expensive $$$
- Yet another benchmark comparing Lambda runtimes
Extra Links
- maxday’s live Lambda benchmarks
- An interesting repo for fine tuning your Lambda settings and costs.