100 millions Lambda execution and guess our AWS $$$ Bill - GoLang

100 millions Lambda execution and guess our AWS $$$ Bill - GoLang

Little Background:-

in 2019 we started moving from server to serverless and started adopting AWS Lambda + GoLang for any new development. My chief architect and current CTO Ben Pyle(Blog) came up with the GoLang pattern which we used in all our Microservices. It was one of the best architectural decisions we have made in recent times.

As of now, we have more than 300+ lambdas deployed in each of our environments. 99% of those lambdas are written in GoLang and the rest 1% in either Node, Python or Java. Most of us had a Java background in my organization when we started in 2019. A couple of GoLang training sessions have helped us in a smooth transition.

This post will help you to understand and implement AWS Lambda written in GoLang.

AWS Bill:-

Here is a screenshot of our recent month's bill. You can see we had 98+ million lambda requests and our total AWS bill for lambda was $108 after our compute savings plan.

  • Keep 3 things in mind when you write Lambda. 1) Runtime 2) Package Size 3) Startup logic.

  • The majority of the Go Lambda that we have in my organization is configured with 128 MB memory (that's the minimum memory we can configure for AWS Lambda).

  • Go executable binary size is approx 8 MB for most of our Lambda. Fewer footprints for runtime.

  • Here are a couple of runtime examples. You can see the COLD START time in this example is 210.65 ms and it used 45 MB of memory. As GoLang compiles code to native code and executes pretty fast, most of the time cold start duration will be a few milliseconds to 1 second.

      Duration: 3.93 ms Billed Duration: 4 ms Memory Size: 
      128 MB Max Memory Used: 44 MB Init Duration: 210.65 ms 
    
      Duration: 1.36 ms Billed Duration: 2 ms Memory Size: 
      128 MB Max Memory Used: 45 MB 
    
      Duration: 16.44 ms Billed Duration: 17 ms Memory Size: 
      128 MB Max Memory Used: 45 MB
    

Examples:-

We follow industry best practices for implementing AWS Lambda.

  1. Optimizing static initialization:- This is the "INIT" code that happens outside of the handler. Initialized your variable or connection to other services.

  2. Single-purpose lambdas:- each lambda has a single responsibility. For any CRUD operation, we implement 4 lambdas (Create, Update, Delete and Get)

This example is implemented using AWS CDK and AWS SAM, whichever is your preferred way, clone the repo from GitHub and try it...

You can download the source code from here

AWS SAM Deployment Instructions:-

  1. change the region name in the template.yaml file if you are deploying other than us-west-2
  Region:
    Type: String
    Description: AWS Region Name
    Default: us-west-2
  1. Changed directory to pattern directory sam
cd sam
  1. From the command line, use the AWS SAM CLI command to deploy the app.
sam deploy --stack-name employee-functions --capabilities CAPABILITY_NAMED_IAM --guided --profile <your profile name>
  1. Once the stack deploys successfully, grab the following API Gateway URL. We will use this URL to execute our API.
Key            APIEndpoint
Description    endpoint of EmployeeApi
Value          https://fy6fpson09.execute-api.us-west-2.amazonaws.com/

AWS CDK Deployment Instructions:-

  1. change the region name in the config.ts file if you are deploying other than us-west-2.
export const getConfig = (): Options => {
    return {
        defaultRegion: "us-west-2",
        apiStageName: "main",
    }
}
  1. Changed directory to pattern directory sam
cd cdk
  1. From the command line, use the AWS CDK CLI command to deploy the app.
cdk deploy --all -a "npx ts-node bin/app.ts" --profile <your profile name>
  1. Once the stack deploys successfully, run the following AWS CLI command to get the API URL. it's using JQ
aws cloudformation list-exports --query "Exports[?Name=='employee-api-url'].Value" --output text --profile <your profile name>

API Execution:-

  1. Add Employee:-
curl -X POST -H 'Content-type: application/json'
https://fy6fpson09.execute-api.us-west-2.amazonaws.com/main/employee \
--data '{
    "Age": 30,
    "FirstName": "Kyle",
    "LastName" : "Gray"  
}'

You will see the following record in DynamoDB.

  1. Get Employee:-
curl -X GET -H 'Content-type: application/json' https://fy6fpson09.execute-api.us-west-2.amazonaws.com/main/employee/2414 | jq

Response:-
{
  "Id": 2414,
  "firstName": "Kyle",
  "lastName": "Gray",
  "age": 30,
  "createdDatetime": "2023-03-06T05:23:59.471574185Z",
  "modifiedDatetime": "0001-01-01T00:00:00Z"
}
  1. Update Employee:-
curl -X PUT -H 'Content-type: application/json' https://fy6fpson09.execute-api.us-west-2.amazonaws.com/main/employee/2414
--data '{
    "Age": 31,
    "FirstName": "Kyle",
    "LastName" : "Gray"
}'

Updated record in DynamoDB

  1. Delete Employee:-
curl -X DELETE -H 'Content-type: application/json' https://fy6fpson09.execute-api.us-west-2.amazonaws.com/main/employee/2414

Cleanup:-

sam delete --stack-name employee-functions --profile <your profile name>
                               OR
cdk destroy --profile <your profile name>

Wrap-Up:-

As you can see how it's easy to understand, implement and deploy GoLang lambda in an AWS environment. I would highly recommend trying it yourself and for your organization. We have a couple of legacy applications where we use an ECS container with 1 CPU and 2 GB RAM, this cost us around $32 per month. Even though the traffic pattern is not consistent, we end up paying $32 per month. In the case of serverless, we pay per use and don't pay anything when there is no traffic. In the next post, I will implement the same example with low code or less code using Step Function + universal target.