In this article, which is part of our AWS Lambda Performance Optimization guide, we discuss using Lambda asynchronous invocations, including features and use cases.
AWS Lambda is a service used to build serverless applications on the AWS cloud. In AWS Lambda, you create Lambda functions to write business logic. A Lambda function can be invoked with the AWS console, Lambda API, AWS SDK, AWS CLI, and AWS toolkits. You can also configure other AWS services such as CloudWatch Events to invoke your function, or you can configure Lambda to read from a Kinesis stream or SQS queue and invoke your function.
In this article
A Lambda function is invoked in one of two ways:
A trigger is used to invoke the Lambda function automatically. A trigger can be an AWS service that is configured to invoke the Lambda function to respond to lifecycle events, or on a schedule. AWS services such as CloudWatch Events can invoke the function based on a schedule. AWS S3 invokes your Lambda function when an object is created or updated.
To trigger a Lambda function from a stream or queue, you need to create an event source mapping. It reads items from an SQS queue, Kinesis stream, or DynamoDB stream, and sends them to the Lambda function.
Now, let’s see how asynchronous invocation works for a Lambda function.
In an asynchronous flow, you don’t need to wait for the response from a Lambda function. AWS services such as AWS S3 or SNS invoke Lambda functions asynchronously by passing events. With Lambda Destinations, a new feature launched by AWS, it can send invocation records to downstream services to chain together.
Asynchronous invocation for a Lambda function requires the invocation type parameter as “Event”. It involves two steps –
$ aws lambda invoke –function-name async-function –invocation-type Event –payload ‘{ “name”: “value” }’ output.json
{
“StatusCode”: 202
}
If it is able to put the event in queue successfully, the output JSON will have a status code of 202, otherwise, it will show an error message. The Lambda service manages the function’s asynchronous event queue and when it fails with errors, it attempts a retry. It tries to run it two more times, with a one-minute wait between the first two attempts, and two minutes between the second and third attempts. These errors can be either returned by the code or runtime.
There might be instances when the function doesn’t have enough concurrency available to process all events. In those cases, additional requests are throttled. If it returns throttling errors (429) and server errors (50X series), Lambda returns the event to the queue and attempts to run the function again for up to 6 hours.
Lambda functions can be written in several languages such as Java, NodeJS, Python, and more. If you use NodeJS and want to implement an asynchronous flow, you would need to use async handlers along with await. AWS Lambda’s async await feature was added after the launch of NodeJS 8.10 runtime. It enables users to enjoy the benefits of Promises using the async/await syntax and write easy-to-maintain clean code.
AWS Lambda async handlers automatically implement the await for Promises so developers don’t have to explicitly use this keyword in the code.
Below is a simple example of await in a Lambda function:
export const handler = async (event, context) => { const response = await someAsyncFunc(); return response; }
This is equivalent to this code:
export const handler = async (event, context) => { return someAsyncFunc(); }
Here are a few important details about how the Node.js Lambda function handler works:
Here is an example of a JavaScript file performing an HTTP request with an async handler, using Promises. The code was shared in the Amazon documentation.
const https = require('https') let url = "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" exports.handler = async function(event) { const promise = new Promise(function(resolve, reject) { https.get(url, (res) => { resolve(res.statusCode) }).on('error', (e) => { reject(Error(e)) }) }) return promise }
If you are using AWS CLI or AWS SDK to invoke an AWS Lambda function, you need to use configuration APIs to manage the asynchronous invocation settings.
You need to use the following API operations:
$ aws lambda put-function-event-invoke-config --function-name error \ --maximum-event-age-in-seconds 3600 --maximum-retry-attempts 2 { "LastModified": 1175686212.411, "FunctionArn": "arn:aws:lambda:ap-east-2:112233445566:function:error:$LATEST", "MaximumRetryAttempts": 0, "MaximumEventAgeInSeconds": 3600, "DestinationConfig": { "OnSuccess": {}, "OnFailure": {} } }
$ aws lambda update-function-event-invoke-config --function-name error \ --destination-config '{"OnFailure":{"Destination": "arn:aws:sqs:us-east-2:112233445566:destination"}}' { "LastModified": 1175691862.482, "FunctionArn": "arn:aws:lambda:us-east-2:112233445566:function:error:$LATEST", "MaximumRetryAttempts": 0, "MaximumEventAgeInSeconds": 3600, "DestinationConfig": { "OnSuccess": {}, "OnFailure": { "Destination": "arn:aws:sqs:ap-east-2:112233445566:destination" } } }
As mentioned above, with an AWS Lambda asynchronous flow, the Lambda service responds back immediately with the status code 202, as long as it is able to place the event in the queue successfully. If, however, anything goes wrong, it doesn’t notify the client. With Lambda Destinations, you can send the records of asynchronous invocations to another service. The Lambda destinations feature enables you to take action for both success and failure conditions. You can choose an SQS queue, an SNS topic, a Lambda function, or EventBridge as destinations.
The benefit of this feature is that you can connect to a destination service from a function without writing a single line of code. The alternative solution is to use AWS Step Functions, which is a bit of overkill for such a simple use case.
An AWS Lambda dead-letter queue works similarly to an on-failure destination. You can configure the function to send discarded events to the dead-letter queue and process them again later. Dead-letter queue is attached with the function’s version configuration so once you publish a version, it’s locked with it. A DLQ can be either an SQS queue or an SNS topic.
Amazon SQS queue – Holds the failed events until they’re retrieved. To retrieve the events, you can configure a Lambda function to read them from the queue and invoke a function.
Amazon SNS topic – Doesn’t have any persistence, so it relays failed events to destinations such as an email address, a Lambda function, or an HTTP endpoint.
Event-driven is the most prevalent architecture used in cloud platforms since the inception of microservice programming, and AWS Lambda is helping to enable passing these events to the integrated services with minimal code written. The AWS Lambda asynchronous features enable implementing more and more use cases to automate data flows based on events. With the destination and dead-letter queue features, it becomes easy to implement error handling and retries without the need to write extra lines of code.