AWS Lambda does not yet support Ruby. Though there are plenty of rumors that AWS is working on it. I’m pretty excited for the day when AWS releases official support for Ruby. Until that day arrives though, we must use a shim in order to add Ruby support to AWS Lambda. A shim is a function written in a natively AWS Lambda supported language that calls out to Ruby. Jets uses a node shim to add Ruby support to AWS Lambda. The neat thing is that Jets adds Ruby support to AWS Lambda at pretty much native speed.
Right off the bat, here’s a performance comparison.
Ruby function speed:
time curl -so /dev/null https://1192eablz8.execute-api.us-west-2.amazonaws.com/dev/ruby_example real 0m0.164s user 0m0.039s sys 0m0.063s
Python function speed:
time curl -so /dev/null https://1192eablz8.execute-api.us-west-2.amazonaws.com/dev/python_example real 0m0.178s user 0m0.047s sys 0m0.054s
In the case above, the Ruby function happened to be faster than the Python function. Generally, it’s the same.
To understand how Jets achieves this level of performance for Ruby support, it is useful to understand a little bit of how AWS Lambda works.
Cold Start Issues
The main issue with shims is the overhead associated with them equates to a cold start every time. Cold starts are a pretty known and have been documented by many people:
- Understanding AWS Lambda Coldstarts
- Solving the Cold Start Problem
- Dealing with cold starts in AWS Lambda
- Everything you need to know about cold starts in AWS Lambda
- Cold starting AWS Lambda functions
If your Lambda function is allocated only 128MB the coldstart overhead can be a few seconds. Even with officially support languages like Java it can take longer. It’s because it takes a little time to load the JVM runtime. Worse yet, if you are using AWS Lambda functions connected to a VPC, then we’re talking about a 10+ seconds overhead penalty 🤦🏻♂️ This is one of the reasons why it is recommended to use Lambda without the VPC feature when possible. Generally, AWS Lambda developers remedy the cold start problem by prewarming their application. Prewarming essentially takes advantage of how the AWS Lambda Execution Context works.
AWS Lambda Execution Context
What is the AWS Lambda Execution Context?
AWS Lambda functions execute in what is called the Execution Context. From the official AWS docs:
Execution Context is a temporary runtime environment that initializes any external dependencies of your Lambda function code, such as database connections or HTTP endpoints. This affords subsequent invocations better performance because there is no need to “cold-start” or initialize those external dependencies.
It takes time to set up an Execution Context and do the necessary “bootstrapping”, which adds some latency each time the Lambda function is invoked. You typically see this latency when a Lambda function is invoked for the first time or after it has been updated because AWS Lambda tries to reuse the Execution Context for subsequent invocations of the Lambda function.
After a Lambda function is executed, AWS Lambda maintains the Execution Context for some time in anticipation of another Lambda function invocation.
Now that cold starts and the Lambda execution context has been covered you can start to see how Jets achieves a high level of performance for Ruby support.
Ruby Support at Native Speed
Jets takes advantage of how AWS Lambda works and it’s Execution Context. Jet loads and keeps the Ruby interpreter in the Execution Context’s memory, essentially giving Ruby native-like speed. Additionally, Jets has built-in Prewarming Support. This makes running Ruby on AWS Lambda pretty much native speed. 🎉
Check out the Live Demo.