Update 2018/12/12: Official Ruby Support was announced at AWS re:Invent 2018 on Nov 29! Jets has switched over to it: Official AWS Ruby Support for Jets 🎉. This article is now out-of-date and kept around only for posterity.

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.

Performance Comparision

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:

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.

More info