Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions lambda-durable-function-chaining-sam/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Function chaining with AWS Lambda durable functions

This pattern demonstrates the **function chaining** workflow pattern using AWS Lambda durable functions. A durable orchestrator chains three Lambda functions sequentially, with automatic checkpointing after each step for fault tolerance.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-durable-function-chaining-sam

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) installed (version 1.141.0+ with DurableConfig support)
* Python 3.13+

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```bash
git clone https://github.com/aws-samples/serverless-patterns
```
2. Change directory to the pattern directory:
```bash
cd serverless-patterns/lambda-durable-function-chaining-sam
```
3. Build and deploy the application:
```bash
sam build
sam deploy --guided
```
4. During the prompts:
* Enter a stack name
* Enter your preferred AWS Region
* Accept the defaults for remaining options

## How it works

This pattern implements the **function chaining** workflow pattern where a durable orchestrator invokes multiple Lambda functions in sequence, passing the output of each step as input to the next.

### Architecture

```mermaid
flowchart LR
A[Input] --> B[Durable Orchestrator]
B -->|context.invoke| C[Step 1: Add Data]
C -->|checkpoint| B
B -->|context.invoke| D[Step 2: Transform]
D -->|checkpoint| B
B -->|context.invoke| E[Step 3: Finalize]
E -->|checkpoint| B
B --> F[Output]

subgraph Lambda durable function
B
end

subgraph Regular Lambda Functions
C
D
E
end
```

The orchestrator uses `context.invoke()` to chain 3 Lambda functions with automatic checkpointing.

### What are AWS Lambda durable functions?

Lambda durable functions enable you to build resilient, long-running workflows with automatic state management. Key capabilities:

- **Checkpoint/Replay**: Each `context.invoke()` creates a checkpoint. If the function fails, it replays from the beginning but skips completed steps using stored results.
- **Fault Tolerance**: Workflows automatically recover from failures without re-executing completed work.
- **Up to 1 Year Execution**: Durable functions can run for extended periods with waits that don't incur compute charges.

### Data Flow

```
Input: {"id": "test-123", "name": "demo", "value": 5}

Step 1 (Add): value = 5 + 10 = 15
Step 2 (Transform): value = 15 × 2 = 30, name = "DEMO"
Step 3 (Finalize): value = 30 + 5 = 35, status = "COMPLETED"

Output: {"value": 35, "final_value": 40, "transformed_name": "DEMO", "status": "COMPLETED"}
```

### Fault Tolerance Example

If the orchestrator fails after Step 2 completes:
1. Lambda automatically retries the orchestrator
2. During replay, Steps 1 and 2 are **skipped** (results loaded from checkpoints)
3. Step 3 executes normally
4. Workflow completes successfully

## Testing

1. Get the orchestrator alias ARN from stack outputs:
```bash
DURABLE_FUNCTION_ARN=$(aws cloudformation describe-stacks \
--stack-name durable-function-chaining \
--query 'Stacks[0].Outputs[?OutputKey==`OrchestratorAliasArn`].OutputValue' \
--output text)
```

2. Invoke the workflow:
```bash
aws lambda invoke \
--function-name "$DURABLE_FUNCTION_ARN" \
--payload '{"id": "test-123", "name": "demo", "value": 5}' \
--cli-binary-format raw-in-base64-out \
response.json

cat response.json
```

3. Expected output:
```json
{
"workflow": "completed",
"input": {"id": "test-123", "name": "demo", "value": 5},
"output": {
"id": "test-123",
"name": "demo",
"step1_completed": true,
"value": 35,
"step2_completed": true,
"transformed_name": "DEMO",
"step3_completed": true,
"status": "COMPLETED",
"final_value": 40
}
}
```

## Cleanup

```bash
sam delete --stack-name durable-function-chaining
```

## Resources

- [Lambda durable functions Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)
- [Durable Execution SDK for Python](https://github.com/aws/aws-durable-execution-sdk-python)
- [AWS Blog: Build multi-step applications with Lambda durable functions](https://aws.amazon.com/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/)

----
Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
78 changes: 78 additions & 0 deletions lambda-durable-function-chaining-sam/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"title": "Function chaining with AWS Lambda durable functions",
"description": "Demonstrates the function chaining pattern using Lambda durable functions with automatic checkpointing and fault-tolerant sequential execution",
"language": "Python",
"level": "200",
"framework": "AWS SAM",
"introBox": {
"headline": "How it works",
"text": [
"This pattern demonstrates the function chaining pattern using Lambda durable functions.",
"A durable orchestrator function chains three Lambda functions sequentially using context.invoke().",
"Each invocation creates an automatic checkpoint, enabling the workflow to resume from the last successful step after failures.",
"Step 1 (Add) initializes data and adds 10 to the input value.",
"Step 2 (Transform) doubles the value and transforms the name to uppercase.",
"Step 3 (Finalize) adds 5 to the value and sets the completion status.",
"The pattern showcases fault-tolerant workflow orchestration without managing state infrastructure."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-function-chaining-sam",
"templateURL": "serverless-patterns/lambda-durable-function-chaining-sam",
"projectFolder": "lambda-durable-function-chaining-sam",
"templateFile": "template.yaml"
}
},
"resources": {
"bullets": [
{
"text": "Lambda durable functions Documentation",
"link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html"
},
{
"text": "Durable Execution SDK for Python",
"link": "https://github.com/aws/aws-durable-execution-sdk-python"
},
{
"text": "AWS Blog: Build multi-step applications with Lambda durable functions",
"link": "https://aws.amazon.com/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/"
}
]
},
"deploy": {
"text": [
"sam build",
"sam deploy --guided"
]
},
"testing": {
"text": [
"Get the orchestrator alias ARN from stack outputs:",
"<code>DURABLE_FUNCTION_ARN=$(aws cloudformation describe-stacks --stack-name durable-function-chaining --query 'Stacks[0].Outputs[?OutputKey==`OrchestratorAliasArn`].OutputValue' --output text)</code>",
"Invoke the workflow:",
"<code>aws lambda invoke --function-name \"$DURABLE_FUNCTION_ARN\" --payload '{\"id\": \"test-123\", \"name\": \"demo\", \"value\": 5}' --cli-binary-format raw-in-base64-out response.json</code>",
"<code>cat response.json</code>",
"Expected output shows value transformation: 5 → +10 (step1) → ×2 (step2) → +5 (step3) = 35"
]
},
"cleanup": {
"text": [
"Delete the stack: <code>sam delete</code>"
]
},
"authors": [
{
"name": "Sahil Bhimjiani",
"image": "https://drive.google.com/file/d/1E2p7S5UtU36x6Sk1xPS3XnSGJyIUoqK7/view?usp=drivesdk",
"bio": "Sahil Bhimjiani is a Solutions Architect at Amazon Web Services.",
"linkedin": "sahil9701"
},
{
"name": "Anup Rajpara",
"image": "https://drive.google.com/file/d/1MqpPNLCqbU4kvvtTspNXZBqD99aVIJI9/view?usp=sharing",
"bio": "Anup is a Sr. Technical Account Manager at Amazon Web Services. He is passionate about serverless & event-driven architectures.",
"linkedin": "anup-rajpara-developer/"
}
]
}
25 changes: 25 additions & 0 deletions lambda-durable-function-chaining-sam/src/orchestrator/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os
from aws_durable_execution_sdk_python import DurableContext, durable_execution


@durable_execution
def lambda_handler(event, context: DurableContext):
"""Durable orchestrator that chains 3 Lambda functions."""
step1_arn = os.environ['STEP1_FUNCTION_ARN']
step2_arn = os.environ['STEP2_FUNCTION_ARN']
step3_arn = os.environ['STEP3_FUNCTION_ARN']

# Step 1: Add initial data
result1 = context.invoke(step1_arn, event, name='step1-add')

# Step 2: Transform data
result2 = context.invoke(step2_arn, result1, name='step2-transform')

# Step 3: Finalize
result3 = context.invoke(step3_arn, result2, name='step3-finalize')

return {
'workflow': 'completed',
'input': event,
'output': result3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aws-durable-execution-sdk-python
8 changes: 8 additions & 0 deletions lambda-durable-function-chaining-sam/src/step1_add/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def lambda_handler(event, context):
"""Step 1: Add initial data fields to the input."""
return {
'id': event.get('id', 'unknown'),
'name': event.get('name', 'default'),
'step1_completed': True,
'value': event.get('value', 0) + 10
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def lambda_handler(event, context):
"""Step 2: Transform and enrich the data."""
return {
**event,
'step2_completed': True,
'value': event.get('value', 0) * 2,
'transformed_name': event.get('name', '').upper()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def lambda_handler(event, context):
"""Step 3: Finalize the data with status."""
return {
**event,
'step3_completed': True,
'status': 'COMPLETED',
'final_value': event.get('value', 0) + 5
}
74 changes: 74 additions & 0 deletions lambda-durable-function-chaining-sam/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Function chaining with AWS Lambda durable functions

Globals:
Function:
Timeout: 30
Runtime: python3.14
MemorySize: 512

Resources:
# Worker Functions
Step1AddFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: step1-add
CodeUri: src/step1_add/
Handler: app.lambda_handler

Step2TransformFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: step2-transform
CodeUri: src/step2_transform/
Handler: app.lambda_handler

Step3FinalizeFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: step3-finalize
CodeUri: src/step3_finalize/
Handler: app.lambda_handler

# Durable Orchestrator Function
OrchestratorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: durable-orchestrator
CodeUri: src/orchestrator/
Handler: app.lambda_handler
Timeout: 900
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is such a high timeout necessary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really

AutoPublishAlias: live
DurableConfig:
ExecutionTimeout: 900
RetentionPeriodInDays: 7
Environment:
Variables:
STEP1_FUNCTION_ARN: !Sub ${Step1AddFunction.Arn}:$LATEST
STEP2_FUNCTION_ARN: !Sub ${Step2TransformFunction.Arn}:$LATEST
STEP3_FUNCTION_ARN: !Sub ${Step3FinalizeFunction.Arn}:$LATEST
Policies:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action: lambda:InvokeFunction
Resource:
- !Sub ${Step1AddFunction.Arn}:*
- !Sub ${Step2TransformFunction.Arn}:*
- !Sub ${Step3FinalizeFunction.Arn}:*
- !GetAtt Step1AddFunction.Arn
- !GetAtt Step2TransformFunction.Arn
- !GetAtt Step3FinalizeFunction.Arn

Outputs:
OrchestratorAliasArn:
Description: Orchestrator function Alias ARN (use this for invocations)
Value: !Ref OrchestratorFunction.Alias
Step1FunctionArn:
Value: !GetAtt Step1AddFunction.Arn
Step2FunctionArn:
Value: !GetAtt Step2TransformFunction.Arn
Step3FunctionArn:
Value: !GetAtt Step3FinalizeFunction.Arn