6
votes

I am using AWS SAM with Python. My goal is to have two Lambdas:

  • Function A: A normal synchronous Lambda which will invoke Function B, then return quickly
  • Function B: A long-running asynchronous Event Lambda

There are a couple other SO questions which deal with this scenario, but as far as I can tell none have touched on how to do it when deploying SAM locally.

Here is my SAM template file:

# template.yaml

Resources:
  FunctionA:
    # PUT /functions/a, should invoke FunctionB asynchronously
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/
      Handler: functions.a
      Runtime: python3.7
      Events:
        FunctionA:
          Type: Api
          Properties:
            Path: /functions/a
            Method: put

  FunctionB:
    # Long-running asynchronous function
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: 'FunctionB'
      CodeUri: api/
      Handler: functions.b
      Runtime: python3.7
      EventInvokeConfig:
        MaximumRetryAttempts: 2
        DestinationConfig:
          OnSuccess:
            Type: SQS
          OnFailure:
            Type: SQS

And my Python lambda handler logic:

# functions.py

def a(event, context):
  boto3.client('lambda').invoke(
    FunctionName='FunctionB',
    InvocationType='Event',
    Payload='some_data'.encode('UTF-8')
  )
  return { "statusCode": 200, "body": {} }

def b(data):
  print("SUCCESS!")

I deploy it locally:

# deploy.sh
sam build
sam local start-api

All is well until this point. When I call PUT /functions/a, I get the following error indicating that Function B could not be invoked from Function A:

[ERROR] ResourceNotFoundException: An error occurred (ResourceNotFoundException) when calling the Invoke operation: Function not found: arn:aws:lambda:us-east-2:[iam-user-id]:function:FunctionB

Has anyone found a fix for this? Here's what I've tried:

  1. Verified that Function B can be invoked successfully via the command line:
sam local invoke FunctionB # works great
  1. Attempted to change the InvocationType=Event to InvocationType=RequestResponse and received the same error
  2. Instantiated the lambda client to reference the local URL
boto3.client('lambda', endpoint_url='http://localhost:3000')
# [ERROR] EndpointConnectionError: Could not connect to the endpoint URL: "http://localhost:3000/2015-03-31/functions/ScheduleShowsAsyncFunction/invocations"
1
I feel your pain, I'm in the same place. I suspect it's because the generated Lambdas are suffixed with an ID to keep them unique between multiple deployments (e.g. FunctionB-123ABC4DE5F6A), so a Lamba named "FunctionB" doesn't exist.Moby Duck

1 Answers

3
votes

You can use sam local start-lambda to run FunctionA, it will emulates on port 3001, and call it from FunctionB with lambda client:

boto3.client('lambda', endpoint_url='http://docker.for.mac.localhost:3001')

You have to use InvocationType=RequestResponse because Event is not yet supported https://github.com/awslabs/aws-sam-cli/pull/749

If FunctionB is long-running and because you can only use RequestResponse you can change lambda client config to increase timeout:

config_lambda = Config(retries={'total_max_attempts': 1}, read_timeout=1200)

lambda_client = boto3.client('lambda',
                             config=config_lambda,
                             endpoint_url='http://docker.for.mac.localhost:3001')