DEV Community

Zahidul Islam
Zahidul Islam

Posted on

Deep dive: 3 ways to invoke AWS Lambda Function

AWS Diagram

AWS Lambda functions are everywhere. We are using it for various use cases. Sometimes we want to invoke Lambda functions over HTTP and other times trigger a function based on some external events. It is extremely useful to understand the different ways we can invoke Lambda functions. Here are 3 different ways we can invoke AWS Lambda function:

Synchronous Invokes

This is the simplest lambda invocation model. When we perform a Lambda invoke an API call, we wait for the function to process the event and return a response or times out. The response includes the function response and additional data.

To perform synchronous invocation we can use the CLI or any of the supported SDKs.

Example of synchronous invoke using the CLI:

aws lambda invoke —function-name MyLambdaFunction —invocation-type RequestResponse —payload  '{ "key": "value" }'

Example of synchronous invoke using the Node.js SDK:

const AWS = require("aws-sdk");

const lambda = new AWS.Lambda();

const params = {
  FunctionName: "MyLambdaFunction",
  InvocationType: "RequestResponse",
  Payload: JSON.stringify({ key: "value" })
};

await lambda.invoke(params).promise();

When we specify the Invocation-type flag or InvocationType parameter value as RequestResponse it instructs AWS to execute our function and wait for the function to complete. One of the drawbacks of synchronous invoke is we are responsible for checking the response and determining whether it is an error and we should retry the invoke.

There are many AWS services that can trigger lambda function synchronously. Here are some of them:

  • ELB (Application Load Balancer)
  • Cognito
  • Lex
  • Alexa
  • API Gateway
  • CloudFront (Lambda@Edge)
  • Kinesis Data Firehose

Asynchronous Invokes

When we invoke a function asynchronously, Lambda sends the event to a queue and returns a success response without additional information. A separate process reads events from the queue and runs our function. To invoke a function asynchronously, we have to set the invocation type parameter to Event.

Example of an asynchronous invokes using the CLI:

aws lambda invoke —function-name MyLambdaFunction —invocation-type Event —payload  '{ "key": "value" }'

Example of an asynchronous invokes using the Node.js SDK:

const AWS = require("aws-sdk");

const lambda = new AWS.Lambda();

const params = {
  FunctionName: "MyLambdaFunction",
  InvokeArgs: JSON.stringify({
    Records: [
      {
        eventVersion: "2.0",
        eventSource: "aws:s3",
        awsRegion: "us-west-2",
        eventTime: "1970-01-01T00:00:00.000Z",
        eventName: "ObjectCreated:Put",
        userIdentity: {
          principalId: "AIDAJDPLRKLG7UEXAMPLE"
        },
        requestParameters: {
          sourceIPAddress: "127.0.0.1"
        },
        responseElements: {
          "x-amz-request-id": "C3D13FE58DE4C810",
          "x-amz-id-2":
            "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
        },
        s3: {
          s3SchemaVersion: "1.0",
          configurationId: "testConfigRule",
          bucket: {
            name: "sourcebucket",
            ownerIdentity: {
              principalId: "A3NL1KOZZKExample"
            },
            arn: "arn:aws:s3:::sourcebucket"
          },
          object: {
            key: "HappyFace.jpg",
            size: 1024,
            eTag: "d41d8cd98f00b204e9800998ecf8427e",
            versionId: "096fKKXTRTtl3on89fVO.nfljtsv6qko"
          }
        }
      }
    ]
  })
};

await lambda.invokeAsync(params).promise();

You can use the above code snippet to write integration tests for your lambda function that gets trigger based on the s3 event.

When we specify the Invocation-type flag or InvocationType parameter value as Event if the function failed AWS will automatically retry the invoke twice.

Here is a list of services that invoke Lambda functions asynchronously:

  • S3
  • SNS
  • SES
  • CloudFormation
  • CloudWatch Logs
  • CloudWatch Events
  • CodeCommit

Pool Based Invokes

Pool-Based invocation model allows us to integrate with AWS Stream and Queue based services. Lambda will poll records from services (Kinesis, SQS, DynamoDB Streams) and invoke our functions. AWS Lambda Event Source Mapping manages the poller and performs Synchronous invokes of our function.

Here is an example mapping of a function (MyLambdaFunction) to a Kinesis stream (arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream) with a batch size of 500:

$ aws lambda create-event-source-mapping --function-name MyLambdaFunction \
--batch-size 500 --starting-position AT_TIMESTAMP --starting-position-timestamp 1541139109 \
--event-source-arn arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream
{
    "UUID": "2b733gdc-8ac3-cdf5-af3a-1827b3b11284",
    "BatchSize": 500,
    "MaximumBatchingWindowInSeconds": 0,
    "ParallelizationFactor": 1,
    "EventSourceArn": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream",
    "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:MyLambdaFunction",
    "LastModified": 1541139209.351,
    "LastProcessingResult": "No records processed",
    "State": "Creating",
    "StateTransitionReason": "User action",
    "DestinationConfig": {},
    "MaximumRecordAgeInSeconds": 604800,
    "BisectBatchOnFunctionError": false,
    "MaximumRetryAttempts": 10000
}

Here is an example lambda function that receives a Kinesis event input and processes the messages:

exports.handler = async event => {
    event.Records.forEach(record => {
        // Kinesis data is base64 encoded so decode here
        const payload = Buffer.from(record.kinesis.data, 'base64').toString('ascii');
        console.log('Decoded payload:', payload);
    });
};

Retry behavior of different invocation model

Invocation Model Retry Behavior
Synchronous None
Asynchronous Twice
Pool Based Retry based on data expiration

Now you know 3 different ways to invoke Lambda functions. If you have questions or suggestions please feel free to let me know in the comments.

Discussion (0)