From 508003b5397ff74a0af74aeca0e12a591e27b40d Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Mon, 16 Mar 2026 14:43:07 +0100 Subject: [PATCH 1/3] added cdk-sfn-dmap-df pattern --- cdk-sfn-dmap-df/README.md | 141 ++++++++ cdk-sfn-dmap-df/bin/cdk-sfn-dmap-df.ts | 20 ++ cdk-sfn-dmap-df/cdk-sfn-dmap-df.png | Bin 0 -> 41720 bytes cdk-sfn-dmap-df/cdk.json | 103 ++++++ cdk-sfn-dmap-df/data/items.json | 302 ++++++++++++++++++ cdk-sfn-dmap-df/example-pattern.json | 66 ++++ cdk-sfn-dmap-df/jest.config.js | 9 + cdk-sfn-dmap-df/lib/cdk-sfn-dmap-df-stack.ts | 160 ++++++++++ .../lib/lambda/item-processor.test.ts | 92 ++++++ cdk-sfn-dmap-df/lib/lambda/item-processor.ts | 86 +++++ cdk-sfn-dmap-df/package.json | 29 ++ cdk-sfn-dmap-df/test/cdk-sfn-dmap-df.test.ts | 36 +++ cdk-sfn-dmap-df/tsconfig.json | 32 ++ 13 files changed, 1076 insertions(+) create mode 100644 cdk-sfn-dmap-df/README.md create mode 100644 cdk-sfn-dmap-df/bin/cdk-sfn-dmap-df.ts create mode 100644 cdk-sfn-dmap-df/cdk-sfn-dmap-df.png create mode 100644 cdk-sfn-dmap-df/cdk.json create mode 100644 cdk-sfn-dmap-df/data/items.json create mode 100644 cdk-sfn-dmap-df/example-pattern.json create mode 100644 cdk-sfn-dmap-df/jest.config.js create mode 100644 cdk-sfn-dmap-df/lib/cdk-sfn-dmap-df-stack.ts create mode 100644 cdk-sfn-dmap-df/lib/lambda/item-processor.test.ts create mode 100644 cdk-sfn-dmap-df/lib/lambda/item-processor.ts create mode 100644 cdk-sfn-dmap-df/package.json create mode 100644 cdk-sfn-dmap-df/test/cdk-sfn-dmap-df.test.ts create mode 100644 cdk-sfn-dmap-df/tsconfig.json diff --git a/cdk-sfn-dmap-df/README.md b/cdk-sfn-dmap-df/README.md new file mode 100644 index 000000000..9234daf02 --- /dev/null +++ b/cdk-sfn-dmap-df/README.md @@ -0,0 +1,141 @@ +# AWS Step Functions Distributed Map with Lambda durable functions + +This pattern demonstrates how to use AWS Step Functions Distributed Map with an Amazon S3 JSON input to fan out across 50 product catalog items, invoking a Lambda durable function for each item. The key technique is using the AWS Step Functions AWS SDK service integration (`CallAwsService` targeting `lambda:invoke`) instead of the optimized Lambda integration. This is necessary because only the raw SDK integration exposes the `DurableExecutionName` parameter, which enables per-item idempotency derived from each product's `itemId`. + +Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cdk-sfn-dmap-df](https://serverlessland.com/patterns/cdk-sfn-dmap-df) + +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) +* [Node.js and npm](https://nodejs.org/) installed (Node.js 22+) +* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) installed +* CDK bootstrapped in your target account/region + +## 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 cdk-sfn-dmap-df + ``` + +3. Install dependencies: + + ```bash + npm install + ``` + +4. Deploy the CDK stack to your default AWS account and region: + + ```bash + cdk deploy + ``` + +5. Note the outputs from the CDK deployment process. These contain the resource names and ARNs used for testing. + +## How it works + +![Architecture Diagram](cdk-sfn-dmap-df.png) + +Architecture flow: +1. AWS Step Functions Distributed Map reads 50 product items from an Amazon S3 JSON file +2. For each item, the map invokes a Lambda durable function via the AWS SDK service integration (`lambda:invoke`) +3. The `DurableExecutionName` is derived from each item's `itemId` using `States.Format`, providing per-item idempotency +4. Each durable function executes a three-operation workflow: + - **`validate-item`** (step) — Validates required fields, checks price > 0, computes a pricing tier (budget / standard / premium) + - **`rate-limit-delay`** (wait) — Pauses 5 seconds to simulate downstream rate limiting. No compute charges during this wait + - **`update-catalog`** (step) — Writes the enriched catalog entry with processing timestamps and a `completed` status +5. Results are written back to Amazon S3 under the `results/` prefix + +### Why the AWS SDK Service Integration? + +AWS Step Functions offers two ways to invoke AWS Lambda: + +| Integration | ARN Pattern | `DurableExecutionName` Support | +|---|---|---| +| Optimized Lambda | `arn:aws:states:::lambda:invoke` | No | +| AWS SDK | `arn:aws:states:::aws-sdk:lambda:invoke` | Yes | + +The optimized integration is simpler but only exposes a subset of the `Lambda.Invoke` API parameters. The AWS SDK integration maps directly to the full `Lambda.Invoke` API, giving access to `DurableExecutionName`. In CDK, this is expressed with `CallAwsService`: + +```typescript +new tasks.CallAwsService(this, 'InvokeDurableFunction', { + service: 'lambda', + action: 'invoke', + parameters: { + 'FunctionName': `${itemProcessor.functionArn}:$LATEST`, + 'InvocationType': 'RequestResponse', + 'DurableExecutionName.$': "States.Format('dmap-item-{}', $.itemId)", + 'Payload.$': '$', + }, + iamResources: [ + `${itemProcessor.functionArn}:$LATEST`, + itemProcessor.functionArn, + ], +}); +``` + +The `DurableExecutionName` is derived from each item's `itemId` using `States.Format`, so re-running the state machine with the same input file produces the same execution names. This means the durable functions return their previously checkpointed results instead of re-executing, providing end-to-end idempotency from AWS Step Functions through to durable function state. + +## Testing + +### Start the State Machine + +```bash +aws stepfunctions start-execution \ + --state-machine-arn \ + --name "catalog-update-$(date +%s)" +``` + +### Monitor Execution + +Monitor the execution in the AWS Step Functions console. The Distributed Map view shows all 50 child executions and their status. Each child invokes the durable function synchronously with a unique `DurableExecutionName`. Results are written back to the Amazon S3 bucket under the `results/` prefix. + +### Verify Deployment + +```bash +# Confirm items.json was deployed to S3 +aws s3 ls s3:///items.json + +# Confirm the state machine exists +aws stepfunctions describe-state-machine \ + --state-machine-arn +``` + +### Re-running for Idempotency + +To demonstrate idempotency, start another execution with the same input: + +```bash +aws stepfunctions start-execution \ + --state-machine-arn \ + --name "catalog-update-rerun-$(date +%s)" +``` + +Since the `DurableExecutionName` values are derived from the `itemId` (which does not change between runs), the durable functions detect that executions with those names already completed and return the previously checkpointed results without re-executing any steps. + +## Cleanup + +1. Delete the stack: + + ```bash + cdk destroy + ``` + +2. Confirm the deletion when prompted. This removes all resources including the Amazon S3 bucket (configured with `autoDeleteObjects` and `RemovalPolicy.DESTROY`) and Amazon CloudWatch log groups. + +--- + +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/cdk-sfn-dmap-df/bin/cdk-sfn-dmap-df.ts b/cdk-sfn-dmap-df/bin/cdk-sfn-dmap-df.ts new file mode 100644 index 000000000..6e8d4cf15 --- /dev/null +++ b/cdk-sfn-dmap-df/bin/cdk-sfn-dmap-df.ts @@ -0,0 +1,20 @@ +#!/usr/bin/env node +import * as cdk from 'aws-cdk-lib/core'; +import { CdkSfnDmapDfStack } from '../lib/cdk-sfn-dmap-df-stack'; + +const app = new cdk.App(); +new CdkSfnDmapDfStack(app, 'CdkSfnDmapDfStack', { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ + + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + // env: { account: '123456789012', region: 'us-east-1' }, + + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); diff --git a/cdk-sfn-dmap-df/cdk-sfn-dmap-df.png b/cdk-sfn-dmap-df/cdk-sfn-dmap-df.png new file mode 100644 index 0000000000000000000000000000000000000000..cc4a902df2575cff392f2d895bdec9ede785f10b GIT binary patch literal 41720 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 clone https://github.com/aws-samples/serverless-patterns", + "Change directory: cd cdk-sfn-dmap-df", + "Install dependencies: npm install", + "Deploy the CDK stack: cdk deploy" + ] + }, + "testing": { + "text": [ + "Start the state machine: aws stepfunctions start-execution --state-machine-arn <StateMachineArn from stack output> --name \"catalog-update-$(date +%s)\"", + "Monitor the execution in the AWS Step Functions console to see all 50 child executions.", + "Re-run with the same input to demonstrate idempotency — durable functions return cached results." + ] + }, + "cleanup": { + "text": ["Delete the stack: cdk destroy"] + }, + "authors": [ + { + "name": "Marco Jahn", + "image": "https://sessionize.com/image/e99b-400o400o2-pqR4BacUSzHrq4fgZ4wwEQ.png", + "bio": "Senior Solutions Architect, Amazon Web Services", + "linkedin": "marcojahn" + } + ] +} diff --git a/cdk-sfn-dmap-df/jest.config.js b/cdk-sfn-dmap-df/jest.config.js new file mode 100644 index 000000000..af8b12534 --- /dev/null +++ b/cdk-sfn-dmap-df/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test', '/lib'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + }, + setupFilesAfterEnv: ['aws-cdk-lib/testhelpers/jest-autoclean'], +}; diff --git a/cdk-sfn-dmap-df/lib/cdk-sfn-dmap-df-stack.ts b/cdk-sfn-dmap-df/lib/cdk-sfn-dmap-df-stack.ts new file mode 100644 index 000000000..19d6f231d --- /dev/null +++ b/cdk-sfn-dmap-df/lib/cdk-sfn-dmap-df-stack.ts @@ -0,0 +1,160 @@ +import * as cdk from 'aws-cdk-lib/core'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs'; +import * as logs from 'aws-cdk-lib/aws-logs'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment'; +import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; +import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks'; +import { Construct } from 'constructs'; +import * as path from 'path'; + +export class CdkSfnDmapDfStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // ===== S3 Bucket for input data ===== + const dataBucket = new s3.Bucket(this, 'CatalogDataBucket', { + bucketName: `sfn-dmap-df-catalog-${cdk.Aws.ACCOUNT_ID}-${cdk.Aws.REGION}`, + removalPolicy: cdk.RemovalPolicy.DESTROY, + autoDeleteObjects: true, + }); + + // Deploy items.json to the bucket + new s3deploy.BucketDeployment(this, 'DeployCatalogData', { + sources: [s3deploy.Source.asset(path.join(__dirname, '..', 'data'))], + destinationBucket: dataBucket, + }); + + // ===== Durable Lambda Function ===== + + const functionLogGroup = new logs.LogGroup(this, 'ItemProcessorLogGroup', { + logGroupName: '/aws/lambda/catalog-item-processor', + retention: logs.RetentionDays.ONE_WEEK, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const itemProcessor = new nodejs.NodejsFunction(this, 'ItemProcessorFunction', { + functionName: 'catalog-item-processor', + description: 'Durable function that validates and updates a single product catalog item', + runtime: lambda.Runtime.NODEJS_22_X, + handler: 'handler', + entry: path.join(__dirname, 'lambda', 'item-processor.ts'), + timeout: cdk.Duration.minutes(1), + memorySize: 256, + logGroup: functionLogGroup, + durableConfig: { + executionTimeout: cdk.Duration.minutes(15), + retentionPeriod: cdk.Duration.days(1), + }, + bundling: { + minify: true, + sourceMap: true, + externalModules: [], + }, + environment: { + NODE_OPTIONS: '--enable-source-maps', + }, + }); + + // ===== Step Functions State Machine ===== + + // Use the AWS SDK service integration (CallAwsService) to invoke the + // durable function. The optimized Lambda integration does not expose the + // DurableExecutionName parameter, so we drop down to the raw + // lambda:invoke API call which gives us full control over all parameters. + const invokeDurableFunction = new tasks.CallAwsService(this, 'InvokeDurableFunction', { + service: 'lambda', + action: 'invoke', + iamAction: 'lambda:InvokeFunction', + parameters: { + // Qualified name required for durable invocations + 'FunctionName': `${itemProcessor.functionArn}:$LATEST`, + 'InvocationType': 'RequestResponse', + // Derive a stable execution name from the item ID for idempotency + 'DurableExecutionName.$': "States.Format('dmap-item-{}', $.itemId)", + 'Payload.$': '$', + }, + iamResources: [ + `${itemProcessor.functionArn}:$LATEST`, + itemProcessor.functionArn, + ], + // Extract just the parsed response payload + resultSelector: { + 'result.$': 'States.StringToJson($.Payload)', + }, + resultPath: '$.processingResult', + }); + + // Distributed Map reads items from S3 and fans out to the invoke task + const distributedMap = new sfn.DistributedMap(this, 'ProcessCatalogItems', { + comment: 'Fan out across 50 product items from S3, invoking a durable function per item', + maxConcurrency: 10, + itemReader: new sfn.S3JsonItemReader({ + bucket: dataBucket, + key: 'items.json', + }), + resultWriterV2: new sfn.ResultWriterV2({ + bucket: dataBucket, + prefix: 'results', + }), + }); + + distributedMap.itemProcessor(invokeDurableFunction, { + mode: sfn.ProcessorMode.DISTRIBUTED, + executionType: sfn.ProcessorType.STANDARD, + }); + + // State machine log group + const stateMachineLogGroup = new logs.LogGroup(this, 'StateMachineLogGroup', { + logGroupName: '/aws/vendedlogs/states/catalog-update-sm', + retention: logs.RetentionDays.ONE_WEEK, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const stateMachine = new sfn.StateMachine(this, 'CatalogUpdateStateMachine', { + stateMachineName: 'catalog-update-distributed-map', + definitionBody: sfn.DefinitionBody.fromChainable(distributedMap), + timeout: cdk.Duration.hours(1), + logs: { + destination: stateMachineLogGroup, + level: sfn.LogLevel.ALL, + includeExecutionData: true, + }, + }); + + // Grant the state machine permission to read from and write to the S3 bucket + dataBucket.grantRead(stateMachine); + dataBucket.grantWrite(stateMachine); + + // Grant the state machine permission to start distributed map child executions + stateMachine.addToRolePolicy(new iam.PolicyStatement({ + actions: ['states:StartExecution'], + resources: [`arn:aws:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:catalog-update-distributed-map`], + })); + + // Grant the state machine the ability to describe and stop child executions + stateMachine.addToRolePolicy(new iam.PolicyStatement({ + actions: ['states:DescribeExecution', 'states:StopExecution'], + resources: [`arn:aws:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:execution:catalog-update-distributed-map/*`], + })); + + // ===== Stack Outputs ===== + + new cdk.CfnOutput(this, 'StateMachineArn', { + value: stateMachine.stateMachineArn, + description: 'ARN of the catalog update state machine', + }); + + new cdk.CfnOutput(this, 'ItemProcessorFunctionArn', { + value: itemProcessor.functionArn, + description: 'ARN of the durable item processor function', + }); + + new cdk.CfnOutput(this, 'DataBucketName', { + value: dataBucket.bucketName, + description: 'S3 bucket containing the product catalog data', + }); + } +} diff --git a/cdk-sfn-dmap-df/lib/lambda/item-processor.test.ts b/cdk-sfn-dmap-df/lib/lambda/item-processor.test.ts new file mode 100644 index 000000000..2616b1127 --- /dev/null +++ b/cdk-sfn-dmap-df/lib/lambda/item-processor.test.ts @@ -0,0 +1,92 @@ +import { LocalDurableTestRunner, OperationStatus, OperationType } from '@aws/durable-execution-sdk-js-testing'; +import { handler } from './item-processor'; + +describe('Product Catalog Item Processor', () => { + beforeAll(() => LocalDurableTestRunner.setupTestEnvironment({ skipTime: true })); + afterAll(() => LocalDurableTestRunner.teardownTestEnvironment()); + + it('should validate, wait, and update a product item', async () => { + const runner = new LocalDurableTestRunner({ + handlerFunction: handler, + }); + + const execution = await runner.run({ + payload: { + itemId: 'PROD-001', + productName: 'Wireless Mouse', + category: 'electronics', + price: 29.99, + }, + }); + + // Verify invocations (initial + replay after wait) + expect(execution.getInvocations().length).toBe(2); + + // Verify final result + const result = execution.getResult() as any; + expect(result.itemId).toBe('PROD-001'); + expect(result.productName).toBe('Wireless Mouse'); + expect(result.category).toBe('electronics'); + expect(result.price).toBe(29.99); + expect(result.priceTier).toBe('standard'); + expect(result.status).toBe('completed'); + expect(result.validatedAt).toBeDefined(); + expect(result.processedAt).toBeDefined(); + + // Verify 3 operations were executed + expect(execution.getOperations()).toHaveLength(3); + + // Verify validate step + const validateStep = runner.getOperation('validate-item'); + expect(validateStep.getType()).toBe(OperationType.STEP); + expect(validateStep.getStatus()).toBe(OperationStatus.SUCCEEDED); + + // Verify wait operation + const waitOp = runner.getOperation('rate-limit-delay'); + expect(waitOp.getType()).toBe(OperationType.WAIT); + expect(waitOp.getStatus()).toBe(OperationStatus.SUCCEEDED); + + // Verify update step + const updateStep = runner.getOperation('update-catalog'); + expect(updateStep.getType()).toBe(OperationType.STEP); + expect(updateStep.getStatus()).toBe(OperationStatus.SUCCEEDED); + }); + + it('should assign budget tier for low-price items', async () => { + const runner = new LocalDurableTestRunner({ + handlerFunction: handler, + }); + + const execution = await runner.run({ + payload: { + itemId: 'PROD-050', + productName: 'Cotton Socks', + category: 'clothing', + price: 9.99, + }, + }); + + const result = execution.getResult() as any; + expect(result.priceTier).toBe('budget'); + expect(result.status).toBe('completed'); + }); + + it('should assign premium tier for high-price items', async () => { + const runner = new LocalDurableTestRunner({ + handlerFunction: handler, + }); + + const execution = await runner.run({ + payload: { + itemId: 'PROD-025', + productName: '4K Monitor', + category: 'electronics', + price: 499.99, + }, + }); + + const result = execution.getResult() as any; + expect(result.priceTier).toBe('premium'); + expect(result.status).toBe('completed'); + }); +}); diff --git a/cdk-sfn-dmap-df/lib/lambda/item-processor.ts b/cdk-sfn-dmap-df/lib/lambda/item-processor.ts new file mode 100644 index 000000000..9a5e092df --- /dev/null +++ b/cdk-sfn-dmap-df/lib/lambda/item-processor.ts @@ -0,0 +1,86 @@ +import { DurableContext, withDurableExecution } from '@aws/durable-execution-sdk-js'; + +interface ProductItem { + itemId: string; + productName: string; + category: string; + price: number; +} + +interface ProcessedProduct { + itemId: string; + productName: string; + category: string; + price: number; + priceTier: string; + validatedAt: string; + status: string; + processedAt: string; +} + +function computePriceTier(price: number): string { + if (price < 25) return 'budget'; + if (price < 100) return 'standard'; + return 'premium'; +} + +export const handler = withDurableExecution( + async (event: ProductItem, context: DurableContext): Promise => { + context.logger.info('Starting product catalog update', { + itemId: event.itemId, + productName: event.productName, + }); + + // Step 1: Validate the product record and enrich with pricing tier + const validated = await context.step('validate-item', async (stepCtx) => { + if (!event.itemId || !event.productName || !event.category) { + throw new Error(`Missing required fields for item ${event.itemId}`); + } + if (event.price <= 0) { + throw new Error(`Invalid price ${event.price} for item ${event.itemId}`); + } + + const priceTier = computePriceTier(event.price); + const validatedAt = new Date().toISOString(); + + stepCtx.logger.info('Item validated', { + itemId: event.itemId, + priceTier, + validatedAt, + }); + + return { priceTier, validatedAt }; + }); + + // Wait: Simulate downstream rate limiting / backpressure + await context.wait('rate-limit-delay', { seconds: 5 }); + + // Step 2: Update the catalog entry + const result = await context.step('update-catalog', async (stepCtx) => { + const processedAt = new Date().toISOString(); + + stepCtx.logger.info('Catalog entry updated', { + itemId: event.itemId, + processedAt, + }); + + return { + itemId: event.itemId, + productName: event.productName, + category: event.category, + price: event.price, + priceTier: validated.priceTier, + validatedAt: validated.validatedAt, + status: 'completed', + processedAt, + }; + }); + + context.logger.info('Product catalog update finished', { + itemId: result.itemId, + status: result.status, + }); + + return result; + } +); diff --git a/cdk-sfn-dmap-df/package.json b/cdk-sfn-dmap-df/package.json new file mode 100644 index 000000000..ecb4dcf33 --- /dev/null +++ b/cdk-sfn-dmap-df/package.json @@ -0,0 +1,29 @@ +{ + "name": "cdk-sfn-dmap-df", + "version": "0.1.0", + "bin": { + "cdk-sfn-dmap-df": "bin/cdk-sfn-dmap-df.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@aws/durable-execution-sdk-js-testing": "^1.0.0", + "@types/jest": "^30", + "@types/node": "^24.10.1", + "aws-cdk": "2.1108.0", + "esbuild": "^0.27.4", + "jest": "^30", + "ts-jest": "^29", + "ts-node": "^10.9.2", + "typescript": "~5.9.3" + }, + "dependencies": { + "@aws/durable-execution-sdk-js": "^1.0.0", + "aws-cdk-lib": "^2.240.0", + "constructs": "^10.5.0" + } +} diff --git a/cdk-sfn-dmap-df/test/cdk-sfn-dmap-df.test.ts b/cdk-sfn-dmap-df/test/cdk-sfn-dmap-df.test.ts new file mode 100644 index 000000000..ff71c454d --- /dev/null +++ b/cdk-sfn-dmap-df/test/cdk-sfn-dmap-df.test.ts @@ -0,0 +1,36 @@ +import * as cdk from 'aws-cdk-lib/core'; +import { Template } from 'aws-cdk-lib/assertions'; +import { CdkSfnDmapDfStack } from '../lib/cdk-sfn-dmap-df-stack'; + +describe('CdkSfnDmapDfStack', () => { + const app = new cdk.App(); + const stack = new CdkSfnDmapDfStack(app, 'TestStack'); + const template = Template.fromStack(stack); + + it('should create a durable Lambda function', () => { + template.hasResourceProperties('AWS::Lambda::Function', { + FunctionName: 'catalog-item-processor', + Runtime: 'nodejs22.x', + }); + }); + + it('should create a Step Functions state machine', () => { + template.hasResourceProperties('AWS::StepFunctions::StateMachine', { + StateMachineName: 'catalog-update-distributed-map', + }); + }); + + it('should create an S3 bucket for catalog data', () => { + template.hasResource('AWS::S3::Bucket', {}); + }); + + it('should configure the durable function with durableConfig', () => { + template.hasResourceProperties('AWS::Lambda::Function', { + FunctionName: 'catalog-item-processor', + DurableConfig: { + ExecutionTimeout: 900, + RetentionPeriodInDays: 1, + }, + }); + }); +}); diff --git a/cdk-sfn-dmap-df/tsconfig.json b/cdk-sfn-dmap-df/tsconfig.json new file mode 100644 index 000000000..bfc61bf83 --- /dev/null +++ b/cdk-sfn-dmap-df/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": [ + "es2022" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "skipLibCheck": true, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} From 879a58bb1391c256fcf3431eb3211e8bf08dabdf Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 16 Mar 2026 15:47:09 +0100 Subject: [PATCH 2/3] Update cdk-sfn-dmap-df/README.md Co-authored-by: Michael Gasch <15986659+embano1@users.noreply.github.com> --- cdk-sfn-dmap-df/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk-sfn-dmap-df/README.md b/cdk-sfn-dmap-df/README.md index 9234daf02..3bbfe30c6 100644 --- a/cdk-sfn-dmap-df/README.md +++ b/cdk-sfn-dmap-df/README.md @@ -1,6 +1,6 @@ # AWS Step Functions Distributed Map with Lambda durable functions -This pattern demonstrates how to use AWS Step Functions Distributed Map with an Amazon S3 JSON input to fan out across 50 product catalog items, invoking a Lambda durable function for each item. The key technique is using the AWS Step Functions AWS SDK service integration (`CallAwsService` targeting `lambda:invoke`) instead of the optimized Lambda integration. This is necessary because only the raw SDK integration exposes the `DurableExecutionName` parameter, which enables per-item idempotency derived from each product's `itemId`. +This pattern demonstrates how to use AWS Step Functions Distributed Map with an Amazon S3 JSON input to fan out across 50 product catalog items, invoking a Lambda durable function for each item. The key technique is using the AWS Step Functions AWS SDK service integration (`CallAwsService` targeting `lambda:invoke`) instead of the optimized Lambda integration. This is currently necessary because only the raw SDK integration exposes the `DurableExecutionName` parameter, which enables per-item idempotency, derived from each product's `itemId`, showcased in this example. Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cdk-sfn-dmap-df](https://serverlessland.com/patterns/cdk-sfn-dmap-df) From c047c4ec7e0cbc6d818a9261d288be1e7d595dae Mon Sep 17 00:00:00 2001 From: Marco Jahn Date: Mon, 16 Mar 2026 16:33:21 +0100 Subject: [PATCH 3/3] fixed review comments --- cdk-sfn-dmap-df/README.md | 12 +- cdk-sfn-dmap-df/cdk-sfn-dmap-df.png | Bin 41720 -> 46189 bytes cdk-sfn-dmap-df/example-pattern.json | 2 + cdk-sfn-dmap-df/lib/lambda/item-processor.ts | 141 ++++++++++--------- 4 files changed, 89 insertions(+), 66 deletions(-) diff --git a/cdk-sfn-dmap-df/README.md b/cdk-sfn-dmap-df/README.md index 3bbfe30c6..67a4573ce 100644 --- a/cdk-sfn-dmap-df/README.md +++ b/cdk-sfn-dmap-df/README.md @@ -35,13 +35,21 @@ Important: this application uses various AWS services and there are costs associ npm install ``` -4. Deploy the CDK stack to your default AWS account and region: +4. Run tests: + + ```bash + npm test + ``` + + This runs the CDK stack assertions and the local durable function tests using `LocalDurableTestRunner`. The durable function tests verify the three-operation workflow (validate → wait → update) and price tier assignment without deploying to AWS. + +5. Deploy the CDK stack to your default AWS account and region: ```bash cdk deploy ``` -5. Note the outputs from the CDK deployment process. These contain the resource names and ARNs used for testing. +6. Note the outputs from the CDK deployment process. These contain the resource names and ARNs used for testing. ## How it works diff --git a/cdk-sfn-dmap-df/cdk-sfn-dmap-df.png b/cdk-sfn-dmap-df/cdk-sfn-dmap-df.png index cc4a902df2575cff392f2d895bdec9ede785f10b..5853983257bfc7cdd6d3fdf043dfad758bd72e03 100644 GIT binary patch literal 46189 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 literal 41720 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 clone https://github.com/aws-samples/serverless-patterns", "Change directory: cd cdk-sfn-dmap-df", "Install dependencies: npm install", + "Run tests: npm test", "Deploy the CDK stack: cdk deploy" ] }, "testing": { "text": [ + "Run local tests: npm test — validates CDK stack assertions and runs the durable function locally using LocalDurableTestRunner (no AWS deployment needed).", "Start the state machine: aws stepfunctions start-execution --state-machine-arn <StateMachineArn from stack output> --name \"catalog-update-$(date +%s)\"", "Monitor the execution in the AWS Step Functions console to see all 50 child executions.", "Re-run with the same input to demonstrate idempotency — durable functions return cached results." diff --git a/cdk-sfn-dmap-df/lib/lambda/item-processor.ts b/cdk-sfn-dmap-df/lib/lambda/item-processor.ts index 9a5e092df..57df7539d 100644 --- a/cdk-sfn-dmap-df/lib/lambda/item-processor.ts +++ b/cdk-sfn-dmap-df/lib/lambda/item-processor.ts @@ -1,86 +1,99 @@ -import { DurableContext, withDurableExecution } from '@aws/durable-execution-sdk-js'; +import { + DurableContext, + withDurableExecution, +} from "@aws/durable-execution-sdk-js"; interface ProductItem { - itemId: string; - productName: string; - category: string; - price: number; + itemId: string; + productName: string; + category: string; + price: number; } interface ProcessedProduct { - itemId: string; - productName: string; - category: string; - price: number; - priceTier: string; - validatedAt: string; - status: string; - processedAt: string; + itemId: string; + productName: string; + category: string; + price: number; + priceTier: string; + validatedAt: string; + status: string; + processedAt: string; } function computePriceTier(price: number): string { - if (price < 25) return 'budget'; - if (price < 100) return 'standard'; - return 'premium'; + if (price < 25) return "budget"; + if (price < 100) return "standard"; + return "premium"; } export const handler = withDurableExecution( - async (event: ProductItem, context: DurableContext): Promise => { - context.logger.info('Starting product catalog update', { - itemId: event.itemId, - productName: event.productName, - }); + async ( + event: ProductItem, + context: DurableContext, + ): Promise => { + context.logger.info("Starting product catalog update", { + itemId: event.itemId, + productName: event.productName, + }); - // Step 1: Validate the product record and enrich with pricing tier - const validated = await context.step('validate-item', async (stepCtx) => { - if (!event.itemId || !event.productName || !event.category) { - throw new Error(`Missing required fields for item ${event.itemId}`); - } - if (event.price <= 0) { - throw new Error(`Invalid price ${event.price} for item ${event.itemId}`); - } + // Note: No try/catch here by design. Errors thrown inside steps are + // checkpointed by the durable SDK — the execution is marked as failed + // and won't re-execute on replay. The Step Functions Distributed Map + // handles failed items at the orchestration level (visible in map results). - const priceTier = computePriceTier(event.price); - const validatedAt = new Date().toISOString(); + // Step 1: Validate the product record and enrich with pricing tier + const validated = await context.step("validate-item", async (stepCtx) => { + if (!event.itemId || !event.productName || !event.category) { + throw new Error(`Missing required fields for item ${event.itemId}`); + } + if (event.price <= 0) { + throw new Error( + `Invalid price ${event.price} for item ${event.itemId}`, + ); + } - stepCtx.logger.info('Item validated', { - itemId: event.itemId, - priceTier, - validatedAt, - }); + const priceTier = computePriceTier(event.price); + const validatedAt = new Date().toISOString(); - return { priceTier, validatedAt }; - }); + stepCtx.logger.info("Item validated", { + itemId: event.itemId, + priceTier, + validatedAt, + }); - // Wait: Simulate downstream rate limiting / backpressure - await context.wait('rate-limit-delay', { seconds: 5 }); + return { priceTier, validatedAt }; + }); - // Step 2: Update the catalog entry - const result = await context.step('update-catalog', async (stepCtx) => { - const processedAt = new Date().toISOString(); + // Wait: Simulate downstream rate limiting / backpressure + await context.wait("rate-limit-delay", { seconds: 5 }); - stepCtx.logger.info('Catalog entry updated', { - itemId: event.itemId, - processedAt, - }); + // Step 2: Update the catalog entry + const result = await context.step("update-catalog", async (stepCtx) => { + const processedAt = new Date().toISOString(); - return { - itemId: event.itemId, - productName: event.productName, - category: event.category, - price: event.price, - priceTier: validated.priceTier, - validatedAt: validated.validatedAt, - status: 'completed', - processedAt, - }; - }); + stepCtx.logger.info("Catalog entry updated", { + itemId: event.itemId, + processedAt, + }); - context.logger.info('Product catalog update finished', { - itemId: result.itemId, - status: result.status, - }); + return { + itemId: event.itemId, + productName: event.productName, + category: event.category, + price: event.price, + priceTier: validated.priceTier, + validatedAt: validated.validatedAt, + status: "completed", + processedAt, + }; + }); - return result; - } + context.logger.info("Product catalog update finished", { + itemId: result.itemId, + status: result.status, + }); + + return result; + }, );