When moving to serverless in AWS there is one service which is almost impossible to avoid. This is AWS Lambda.

I’m a big fan of this service and try to do most of the custom work with this. Lets talk about the advantages and disadvantages of this one.

The “function in the cloud”

The unit of work of Lambda is a function. Actually a file with a function but we will come to that. Lets start with something very simple in a random programming language:

function hello(name: string) {
  return 'Hello ' + name;
}

We could call this like:

console.log(hello('Joost'));
// Output "Hello Joost"

When using AWS Lambda you can make this function available in the AWS cloud environment. Within cloud you can execute (or invoke) this function like this:

$ aws Lambda invoke --function-name hello --payload "Joost"

In the data response you will see your value:

{
  "data": "Hello Joost"
}

Sounds simple right? Actual, its even more simpler. This example uses the AWS CLI but it could be any other way to execute the invoke command like through an AWS SDK which is available in basically every programming language or directly as an API call.

A function can contain a full application

Even though it is called a function you can (but probably should not) write a complete application in this function.

AWS will always run your function when you need it to run. It will take care of maintaining a safe sandbox environment. Due to the small and predictable unit of work this function runtime can be executed on any available server and will therefor always be available without the worry for functional management.

Integrations

Having a “function in the cloud” might not be very useful on its own. However, AWS is using this function as one of the most common integrations for other services. There are many integrations but lets pick out a couple:

  • API Gateway (i.e. Execute a function on a HTTP event)
  • Event bridge (i.e. Execute a function on a cron event)
  • Simple Queue Service (i.e. Execute a function to process an item in a Queue)
  • Cognito (i.e. Execute a function to manipulate the JWT payload)

Lambda is the default customization point and the default serverless compute unit of work.

Scaling and concurrency

Most of the time you will get a “state” together with a function invocation. This makes the function perfect for parallel processing.

Run thousands of implementations simultaneous

In theory there is no real limit to the amount of parallel processing of a function (in practice the default is on 1000 per account but you can increase this).

A nice example for this is:

Event analyzing queue

Assuming we have a huge amount of user data to process. We add the following components:

  • event queue to receive all events
  • Lambda to analyze a single event and generating a metric
  • metrics buffer queue to post results
  • Lambda to aggregate the metrics
  • dynamo to store the aggregated metrics

The functions inside the 2 Lambdas are very straight forward. We do not know up front how many events we will receive in which time frame. The AWS Lambda service will spin up as many analyzing Lambdas as needed to keep emptying the queue. This could be just one every 5 minutes or 100 in 10 seconds.

Be aware that AWS Lambda has 1 invocation per instance. This means that if a second invocation starts and the first instance is still executing it will spin-up a new instance. Instances will be reused to save on concurrent instances.

This is well described in understanding Lambda concurrency

Runtimes

Lambda has many out-of-the-box runtimes available. You can think of:

  • nodejs
  • python
  • java

However, if you can produce a binary which runs on amazon linux you can basically use every possible language. This is important when we talk about performance and maintainability.

Choosing the right runtime can drastically reduce your cloud costs

Choosing the right runtime can drastically reduce your cloud costs. Lambda pricing is base on:

  • time of execution
  • runtime memory

Some runtimes like rust and go excellent in both area’s and are probably better for high loads then runtimes like python, java and node.

Cold start

Cold start is the time spend by AWS Lambda to:

  • download the distribution package zip file
  • extract the zip file
  • start your runtime

“We can’t use Lambda because of the cold start 😱!”

I’ve heard the above quote many times. This is indeed a concept you need to be aware of. Especially when working with reactive systems it can be annoying or even problematic.

In the understanding Lambda concurrency you can read about cold start as well (this is the INIT part).

To reduce the cold-start time you should keep the distribution package small and use a fast runtime.

Pricing model

The pricing model of Lambda is very simple. Next to the number of invocations it is basically based on 2 factors:

  • Execution time
  • Requested runtime memory

Execution time

With Lambda you pay for the execution time per millisecond. This means that it really pays off to keep your execution time short.

To be cost effective with Lambda try to focus on pure logic execution. Every millisecond of idle time will cost you. Most of the time calling a few API’s. In that case make sure they are fast.

Use binary compiled languages

This is also a reason to use binary compiled languages instead of interpreted languages. For the interpreted languages you are constantly paying for the runtime overhead.

Requested runtime memory

This is the amount of memory which is reserved for your function execution. The higher the memory, the higher the price per millisecond. Memory heavy applications will cost you (i.e. java).

There is one important side-note to make here.

More memory means more processing power

When you specify a higher amount of memory for your function, Lambda will run the function on a better instance. This means that you have more and faster CPU cores available. So, when you design your application in a way that it can utilize this power, you can reduce the execution time and therefor the total costs.

There are many articles written with this in mind.

When NOT to use

So far it looks like Lambda could be your one-stop serverless compute engine. But there are some exceptions.

Slow backend API

The main exception is when you have a lot of idle time or unpredictable idle time. This happens for example to call a slow backend API or when you allow 3rd parties to specify an API endpoint.

In this case the cloud costs can increase dramatically and it is probably better to run this part of your application in a container. This is completely up to you but you could end up with something like this:

Container with Lambda processor

In this design we use a 24/7 container instance to handle API calls. It will have some logic to execute a slow target server.

Container with Lambda processor

In this case we use the container mainly as a passthrough and orchestration tool. It waits on the slow server response and uses Lambda to do the cpu intensive processing. With this scenario you can keep a very small container which can handle millions of calls because we do not use the CPU for processing. Only for connection preservation.

End note

Thanks for reading, I hope it was useful. Please drop me a note on linked in when you have additional questions or remarks.

~ Joost van der Waal (Cloud guru)