From 821e35702922429e36f62bde2f0e5c9093f02995 Mon Sep 17 00:00:00 2001 From: Sidharth Kothari Date: Sun, 8 Feb 2026 10:36:54 +0530 Subject: [PATCH 1/2] Initial Commit --- .../EventBridge_DF_architecture.png | Bin 0 -> 41120 bytes .../README.md | 97 ++++++++++++++++++ .../example-pattern.json | 63 ++++++++++++ .../src/lambda_function.py | 41 ++++++++ .../src/requirements.txt | 1 + .../template.yaml | 62 +++++++++++ 6 files changed, 264 insertions(+) create mode 100644 lambda-durable-eventbridge-cron-python-sam/EventBridge_DF_architecture.png create mode 100644 lambda-durable-eventbridge-cron-python-sam/README.md create mode 100644 lambda-durable-eventbridge-cron-python-sam/example-pattern.json create mode 100644 lambda-durable-eventbridge-cron-python-sam/src/lambda_function.py create mode 100644 lambda-durable-eventbridge-cron-python-sam/src/requirements.txt create mode 100644 lambda-durable-eventbridge-cron-python-sam/template.yaml diff --git a/lambda-durable-eventbridge-cron-python-sam/EventBridge_DF_architecture.png b/lambda-durable-eventbridge-cron-python-sam/EventBridge_DF_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..df6f7165a27b79148f409ef9b42e691928958ab6 GIT binary patch literal 41120 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

`)aT>GBM zju=)S4b9MUDX~if(P2zu(D9=xK%KhDnTv=%=>7r(4S4{#_;SpgbnCftJ(#Ut4jhaJ z$*4w(f>nOo^M0UyFWK>K-o~l^S;nYN{^ta#vLAL*kf*UN@6&R5u0fRJRW+a30HT*DFq;$ytLm!qET1kJgI9gjFH*4h&IV{ninsD9;7&0D`JTec zWq$w6i>(9jYzecnlJ;Z?;Vp+hmS2L*#F1kkCmn;|LoLqX`TIYr@*hUhauhojl>_~V zn3G3&<&({UeEBO6bo%G=9ZQEq+^dOVuL`1^rDyj9MNVn_{Rm{Enge2LO=3~-q>m^7 znU*bXPN-^lWPalXSY#u}b8m;>_~PR!0@Xt zBC^t6=`ISqX-_*nKKnC$W--}H zv$+Y0JSUu4WYPVPja-~`Q82xV{eQN9Nc3qTW%a{w#(y0naPCAM#NkJNvB-ZN8eq}n zf&0@(&i(t)fIz=8+(LS3_3->Xe2CMeFl6U;ip`%5>d)0`At||*gI@nSYQTU0?=Agz xl>R%H{=4G-&%T7J+esF^Mf)FxDMXu!Kvmf9GFz@O;Y0%dXsPR}m8sf>{yzjUoWlSB literal 0 HcmV?d00001 diff --git a/lambda-durable-eventbridge-cron-python-sam/README.md b/lambda-durable-eventbridge-cron-python-sam/README.md new file mode 100644 index 000000000..a234e5742 --- /dev/null +++ b/lambda-durable-eventbridge-cron-python-sam/README.md @@ -0,0 +1,97 @@ +# EventBridge Cron to Durable Lambda Function + +This pattern demonstrates how to trigger a durable Lambda function using EventBridge on a cron schedule. The Lambda function uses the AWS Durable Execution SDK to implement a multi-step workflow with checkpointing and automatic replay capabilities. + +Learn more about this pattern at Serverless Land Patterns: << Add the live URL here >> + +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. + +## Architecture + +![Architecture](EventBridge_DF_architecture.png) + +This architecture demonstrates a serverless cron job implementation using EventBridge and durable Lambda functions. An EventBridge rule configured with a cron expression triggers the durable Lambda function every minute. The Lambda function uses the AWS Durable Execution SDK to implement a multi-step workflow that can span multiple invocations through checkpointing - when `context.wait()` is called, the function suspends execution and creates a checkpoint, then resumes from that point in a subsequent invocation without re-executing previous steps. + +## 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 Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +* [Python 3.14](https://www.python.org/downloads/) installed and available in your PATH + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd lambda-durable-eventbridge-cron-python-sam + ``` +1. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yaml file: + ``` + sam build + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +This pattern creates: + +1. **Durable Lambda Function**: A Python 3.14 Lambda function that uses the AWS Durable Execution SDK to implement a multi-step workflow with automatic checkpointing and replay capabilities. + +2. **EventBridge Cron Rule**: An EventBridge rule configured with `cron(* * * * ? *)` that triggers the Lambda function every minute. + +3. **Function Versioning**: The Lambda function uses `AutoPublishAlias: live` to automatically publish a new version on each deployment and point the `live` alias to it. + +4. **Targeted Invocation**: The EventBridge rule specifically targets the published version via the alias as it is a best practice to use numbered versions or aliases for production durable functions rather than $LATEST. + +### Durable Execution Flow + +The Lambda function implements a durable workflow with three steps: + +1. **Data Fetching**: Simulates fetching data from an external API (checkpointed) +2. **Wait Period**: Suspends execution for 10 seconds using `context.wait()` - no compute costs during wait +3. **Data Processing**: Processes the fetched data and returns results + +**Execution Pattern**: +- **Invocation 1**: `fetch_data()` runs → checkpoint created → `context.wait()` suspends execution +- **Invocation 2**: `fetch_data()` replays from checkpoint (no re-execution) → wait completes → `process_data()` runs → workflow completes + +This demonstrates how durable functions can span multiple Lambda invocations while maintaining state and avoiding redundant work through checkpointing. + +## Testing + +1. After deployment, the EventBridge rule will automatically trigger the Lambda function every minute. + +2. Monitor the function execution in CloudWatch Logs: + ```bash + aws logs tail /aws/lambda/[FUNCTION_NAME] --follow + ``` + +3. You should observe the durable execution pattern: + - First invocation: "Fetching data from external API..." followed by suspension + - Second invocation: "Processing data..." (fetch_data skipped due to checkpoint) + +4. You can also see the durable execution section in the Lambda function console to get a detailed overview of each execution step in the execution. + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` +---- +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/lambda-durable-eventbridge-cron-python-sam/example-pattern.json b/lambda-durable-eventbridge-cron-python-sam/example-pattern.json new file mode 100644 index 000000000..0e08a12a1 --- /dev/null +++ b/lambda-durable-eventbridge-cron-python-sam/example-pattern.json @@ -0,0 +1,63 @@ +{ + "title": "EventBridge Cron to Durable Lambda Function", + "description": "Create a durable Lambda function triggered by EventBridge on a cron schedule using AWS SAM.", + "language": "Python", + "level": "200", + "framework": "AWS SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to create a durable Lambda function that is triggered by EventBridge on a cron schedule. The Lambda function uses the AWS Durable Execution SDK to implement a multi-step workflow with automatic checkpointing and replay capabilities.", + "The durable execution pattern allows Lambda functions to span multiple invocations while maintaining state. When the function calls context.wait(), it suspends execution and creates a checkpoint. A subsequent invocation resumes from the checkpoint without re-executing previous steps.", + "This pattern deploys a Lambda function with Python 3.14 runtime, an EventBridge rule with cron schedule, and uses function versioning to ensure the cron trigger targets a published version rather than $LATEST." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-eventbridge-cron-python-sam", + "templateURL": "serverless-patterns/lambda-durable-eventbridge-cron-python-sam", + "projectFolder": "lambda-durable-eventbridge-cron-python-sam", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Durable Execution SDK for Python", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-execution.html" + }, + { + "text": "EventBridge Rules and Schedules", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rules.html" + }, + { + "text": "Lambda Function Versioning and Aliases", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html" + } + ] + }, + "deploy": { + "text": [ + "sam build", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Sidharth Kothari", + "image": "https://drive.google.com/file/d/1sUXFJLHYuCmadcu4Q7mhb0mBnWfTcrtT/view", + "bio": "Cloud Engineer II at AWS with deep expertise in serverless, event-driven and microservice-based solutions. Passionate about building scalable, secure and distributed applications that help organizations modernize their infrastructure and accelerate innovation.", + "linkedin": "sidharthkothari" + } + ] +} \ No newline at end of file diff --git a/lambda-durable-eventbridge-cron-python-sam/src/lambda_function.py b/lambda-durable-eventbridge-cron-python-sam/src/lambda_function.py new file mode 100644 index 000000000..906e60b59 --- /dev/null +++ b/lambda-durable-eventbridge-cron-python-sam/src/lambda_function.py @@ -0,0 +1,41 @@ +from aws_durable_execution_sdk_python.config import Duration +from aws_durable_execution_sdk_python.context import DurableContext, StepContext, durable_step +from aws_durable_execution_sdk_python.execution import durable_execution + +@durable_step +def fetch_data(step_context: StepContext) -> dict: + """Simulate fetching data from an external source.""" + # This runs only once — on replay, the checkpointed result is returned. + print("Fetching data from external API...") + return {"items": [1, 2, 3], "source": "external-api"} + + +@durable_step +def process_data(step_context: StepContext, data: dict) -> dict: + """Simulate processing the fetched data.""" + print("Processing data...") + total = sum(data["items"]) + return {"total": total, "source": data["source"], "status": "processed"} + + +@durable_execution +def lambda_handler(event: dict, context: DurableContext) -> dict: + """ + Durable function triggered by EventBridge cron. + + Execution flow: + Invocation 1: fetch_data runs → checkpoint → wait suspends execution + Invocation 2: fetch_data replays from cache → wait completes → process_data runs → done + """ + # Step 1: Fetch data (checkpointed, won't re-execute on replay) + data = context.step(fetch_data()) + + # Step 2: Wait 10 seconds (Lambda suspends, no idle compute cost) + context.wait(Duration.from_seconds(10)) + + # Step 3: Process the data + result = context.step(process_data(data)) + + return result + + diff --git a/lambda-durable-eventbridge-cron-python-sam/src/requirements.txt b/lambda-durable-eventbridge-cron-python-sam/src/requirements.txt new file mode 100644 index 000000000..ade9ff0ef --- /dev/null +++ b/lambda-durable-eventbridge-cron-python-sam/src/requirements.txt @@ -0,0 +1 @@ +aws-durable-execution-sdk-python==1.3.0 \ No newline at end of file diff --git a/lambda-durable-eventbridge-cron-python-sam/template.yaml b/lambda-durable-eventbridge-cron-python-sam/template.yaml new file mode 100644 index 000000000..24c1ccff3 --- /dev/null +++ b/lambda-durable-eventbridge-cron-python-sam/template.yaml @@ -0,0 +1,62 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Durable Lambda function triggered by an EventBridge cron schedule every 1 minute + +Globals: + Function: + Timeout: 900 + MemorySize: 256 + Runtime: python3.14 + +Resources: + # Durable Lambda function — publishes a new version on each deploy + DurableCronFunction: + Type: AWS::Serverless::Function + Properties: + Handler: lambda_function.lambda_handler + CodeUri: src/ + AutoPublishAlias: live + DurableConfig: + ExecutionTimeout: 900 + RetentionPeriodInDays: 7 + Description: Durable execution Lambda triggered every minute by EventBridge + Policies: + - Statement: + - Effect: Allow + Action: + - lambda:InvokeFunction + Resource: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:* + + # EventBridge rule — every minute cron schedule + HourlyCronRule: + Type: AWS::Events::Rule + Properties: + Description: Triggers the durable Lambda function every minute + ScheduleExpression: cron(* * * * ? *) + State: ENABLED + Targets: + - Arn: !Ref DurableCronFunction.Alias + Id: DurableCronFunctionTarget + + # Permission for EventBridge to invoke the published alias + HourlyCronRulePermission: + Type: AWS::Lambda::Permission + Properties: + FunctionName: !Ref DurableCronFunction.Alias + Action: lambda:InvokeFunction + Principal: events.amazonaws.com + SourceArn: !GetAtt HourlyCronRule.Arn + +Outputs: + DurableCronFunctionAliasArn: + Description: ARN of the live alias (published version) + Value: !Ref DurableCronFunction.Alias + PublishedVersion: + Description: Latest published version number + Value: !Ref DurableCronFunction.Version + DurableCronFunctionName: + Description: Name of the durable cron Lambda function + Value: !Ref DurableCronFunction + HourlyCronRuleArn: + Description: ARN of the EventBridge every-minute cron rule + Value: !GetAtt HourlyCronRule.Arn From cd8343208dcb76c113c286f9aa7d582e3a1e45a9 Mon Sep 17 00:00:00 2001 From: Sidharth Kothari Date: Sun, 8 Feb 2026 10:59:00 +0530 Subject: [PATCH 2/2] Updates the links for references in the example pattern --- .../example-pattern.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lambda-durable-eventbridge-cron-python-sam/example-pattern.json b/lambda-durable-eventbridge-cron-python-sam/example-pattern.json index 0e08a12a1..2fdd0f29a 100644 --- a/lambda-durable-eventbridge-cron-python-sam/example-pattern.json +++ b/lambda-durable-eventbridge-cron-python-sam/example-pattern.json @@ -1,5 +1,5 @@ { - "title": "EventBridge Cron to Durable Lambda Function", + "title": "EventBridge Cron to durable Lambda function", "description": "Create a durable Lambda function triggered by EventBridge on a cron schedule using AWS SAM.", "language": "Python", "level": "200", @@ -7,9 +7,9 @@ "introBox": { "headline": "How it works", "text": [ - "This sample project demonstrates how to create a durable Lambda function that is triggered by EventBridge on a cron schedule. The Lambda function uses the AWS Durable Execution SDK to implement a multi-step workflow with automatic checkpointing and replay capabilities.", + "This sample project demonstrates how to create a durable Lambda function that is triggered by EventBridge on a cron schedule. The Lambda function uses the AWS durable Execution SDK to implement a multi-step workflow with automatic checkpointing and replay capabilities.", "The durable execution pattern allows Lambda functions to span multiple invocations while maintaining state. When the function calls context.wait(), it suspends execution and creates a checkpoint. A subsequent invocation resumes from the checkpoint without re-executing previous steps.", - "This pattern deploys a Lambda function with Python 3.14 runtime, an EventBridge rule with cron schedule, and uses function versioning to ensure the cron trigger targets a published version rather than $LATEST." + "This pattern deploys a durable Lambda function with Python 3.14 runtime, an EventBridge rule with cron schedule, and uses function versioning to ensure the cron trigger targets a published version rather than $LATEST." ] }, "gitHub": { @@ -23,16 +23,16 @@ "resources": { "bullets": [ { - "text": "AWS Durable Execution SDK for Python", - "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-execution.html" + "text": "AWS Lambda durable functions", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html" }, { - "text": "EventBridge Rules and Schedules", - "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rules.html" + "text": "Invoking AWS Lambda durable functions", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-invoking.html" }, { - "text": "Lambda Function Versioning and Aliases", - "link": "https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html" + "text": "AWS durable execution SDK for Python", + "link": "https://github.com/aws/aws-durable-execution-sdk-python" } ] },