In this article I will explain in more depth how this works and how it is even
possible to run Sinatra (and other small Rack applications) on AWS Lambda

Originally I wanted to create step by step Sinatra - AWS Lambda manual from
scratch but guys at AWS done really good job with the mentioned example repo. I couldn’t produce
anything that would add more value.

AWS Lambda - how it works

Lamda is a product from AWS (Amazon
Web Services) in which you run code without provisioning or managing servers.
You pay only for the compute time you consume - there is no charge when your code is not running.

I literally copy paste this definition from their website

So what it means is you write Ruby script, you load the code to AWS Lamda and
then when you trigger it. That will:

spin up Lambda function

run your script code (e.g. write some record to DB, process some payment, send an email)

spin down Lamda (after some time of inactivity)

Serverless folks will hate me for using the words “spin up”. In reality YOU are not spinning up anything as the AWS Lamda function is Function as a Service (FaaS)
therefore provisioning is taken care of by AWS. All you need to do is invoke it. If no availible Lambda is up it will spin up (“cold start”) automatically.

The price of execution depends of how much memory you allocate to the
Lambda execution and how long it will took to finish your code
execution. But generally we are talking about like $0.00001 per execution (pricing)

Duration is calculated from the time your code begins executing until it returns or otherwise terminates, rounded up to the nearest 100ms. The price depends on the amount of memory you allocate to your function.

If you need to run the same Lambda function simultaneously 10 times you have to
spin up 10 separate Lamba invocations of the same functionality.

However next execution of AWS lamda function will be executed on waiting
AWS Lambda (no spin up is necessary you just invoke code/params on span-up one that is
not performing anything)

spin up Lambda function

call handler #lambda_handler- from this point is when you start being charged $$$

run your code

return value - from this point is when you stop being charged

call handler #lambda_handler- from this point is when you start being charged $$$

run your code

return value - from this point is when you stop being charged

spin down Lamda (after some time of inactivity)

The important part is that you are not able guarantee that if you load something in the memory
for next execution, it will get picked up. So if you load bunch of
dependencies/configurations into the memory they may not be there for the next
invocation.

Therefore “cold start” of AWS Lambda may take some time if your code is
too heavy.

Also this depends on what programming lang you use in AWS Lambda.
Java, C# would notice slower cold starts far more than Python or Ruby.

Bare with me I’ll get to the point why all this is important in a
minute.

I’ll describe the entire flow of the code at the bottom of this
article

That Brings us to the main point of how this works:

AWS API Gateway will proxy any/every request to this one AWS Lambda that will spin up and execute one route of the Sinatra application. Once the response is returned to the API Gateway Lambda will die.

That means if this Sinatra app needs to receive 50 requests, it will
spin up 50 AWS Lambda Functions.

And only next requests “may be” executed on loaded up Lambdas with
Sinatra dependancies in memory.

If there are 1000 requests concurrently, AWS will try to run 1000
invocations for the same Lambda function. However, if it’s 1000
requests/second, and each request only needs 200 ms to process, there could
only be 200 concurrent invocations at any point of time. Reference

Can I run Ruby on Rails on AWS Lambda ?

Sinatra is build on top of Rack. Rails is build on top Rack. So there should not be any problem running entire Ruby on Rails project on AWS Lambda right ?

You need to realize that serverless is next level of Microservices. It’s
not designed for Monolith Applications. And although Rails can be used
to some degree in Microservices it’s primary goal is Monolith ( e.g.: Majestic Monolith by D.H.H)

By definition Rails have lot of tools and dependencies that are freaking
awesome in long running Monolith but makes zero sense in Serverless.

If you manage to make AWS Lambda work with Rails you would discover that
every request is taking ridiculous amount of time because every request
would have to load entire Rails and your code to memory .

You need to have your Lambda functions load up fast otherwise you will
have slow response times and pay more than you would with server
running 24h a day.

Same would apply for large Sinatra Applications.

If your Sinatra application has 5 - 10 routes and maybe 5 small extra
gems then you are fine. If your Sinatra application is a monolith with like 100 routes
with 50 dependencies then you will have same problem as with Rails.

So no Rails is not Designed for this. Don’t do it even if you
theoretically could.

Full flow of AWS Sinatra Serverless example

We are done with the main article (you don’t need to read this). This section is more for those who want
to try out the example repo and learn how it works step by step.

API Gateway is called

During the time this article was being published author of
serverless-sinatra-sample pushed support for AWS ALB (application load balacer) commit which is even cooler.

AWS API Gatway see that you are using the /Prod pipeline (as you can have /Stage or /Prod) so it will pass the request to the Prod stage

In Prod Stage will recognize you are calling /hello-world now the project has configured a API Gateway to pass all request * to AWS Lambda executing our Sinatra app. Therefore this Proxy will trigger our Sinatra AWS Lamda for any request (/, /hello-world, /api/feedback) any method (GET, POST,…)

Sinatra will carry on application execution as normal Sinatra webserver. That means it will find the get ‘/hello-world’ route and execute the code.

One important point to mention here is the before block we do at the
top of the file (here)

before do
if request.body.size > 0
request.body.rewind
@params = Sinatra::IndifferentHash.new
@params.merge!(JSON.parse(request.body.read))
end
end

As in first step we were setting the ENV variable rack.input with
the body of API Gateway, Sinatra would not effectively parse the body as
it would be in raw JSON format. That’s why this block will parse the
JSON to hash as would Sinatra normally do with HTTP form params

This will be needed when you do POST /api/feedback

and finally lambda.rb will pass the response of the rack
application back to API Gateway via return value
here