{"id":13505617,"url":"https://github.com/kolomied/cdk-stepfunctions-patterns","last_synced_at":"2025-03-16T22:31:28.880Z","repository":{"id":57195499,"uuid":"287946997","full_name":"kolomied/cdk-stepfunctions-patterns","owner":"kolomied","description":"A set of Step Functions high-level resiliency patterns.","archived":false,"fork":false,"pushed_at":"2020-12-15T21:41:20.000Z","size":208,"stargazers_count":67,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-16T06:12:18.339Z","etag":null,"topics":["aws","cdk","pattern","pattern-library","patterns","resiliency","resiliency-patterns","stepfunctions"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kolomied.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-16T13:16:09.000Z","updated_at":"2025-03-03T17:51:57.000Z","dependencies_parsed_at":"2022-09-16T10:41:43.036Z","dependency_job_id":null,"html_url":"https://github.com/kolomied/cdk-stepfunctions-patterns","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kolomied%2Fcdk-stepfunctions-patterns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kolomied%2Fcdk-stepfunctions-patterns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kolomied%2Fcdk-stepfunctions-patterns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kolomied%2Fcdk-stepfunctions-patterns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kolomied","download_url":"https://codeload.github.com/kolomied/cdk-stepfunctions-patterns/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243944708,"owners_count":20372850,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","cdk","pattern","pattern-library","patterns","resiliency","resiliency-patterns","stepfunctions"],"created_at":"2024-08-01T00:01:10.704Z","updated_at":"2025-03-16T22:31:28.608Z","avatar_url":"https://github.com/kolomied.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","High-Level Frameworks"],"sub_categories":["Multi-accounts setup"],"readme":"# cdk-stepfunctions-patterns [![Mentioned in Awesome CDK](https://awesome.re/mentioned-badge.svg)](https://github.com/kolomied/awesome-cdk)\n![build](https://github.com/kolomied/cdk-stepfunctions-patterns/workflows/build/badge.svg)\n![jsii-publish](https://github.com/kolomied/cdk-stepfunctions-patterns/workflows/jsii-publish/badge.svg)\n![downloads](https://img.shields.io/npm/dt/cdk-stepfunctions-patterns)\n\n[![npm version](https://badge.fury.io/js/cdk-stepfunctions-patterns.svg)](https://badge.fury.io/js/cdk-stepfunctions-patterns)\n[![PyPI version](https://badge.fury.io/py/cdk-stepfunctions-patterns.svg)](https://badge.fury.io/py/cdk-stepfunctions-patterns)\n[![NuGet version](https://badge.fury.io/nu/Talnakh.StepFunctions.Patterns.svg)](https://badge.fury.io/nu/Talnakh.StepFunctions.Patterns)\n\n*cdk-stepfunctions-patterns* library is a set of [AWS CDK](https://aws.amazon.com/cdk/) constructs that provide \nresiliency patterns implementation for AWS Step Functions.\n\nAll these patterns are *composable*, meaning that you can combine them together to create\nquite complex state machines that are much easier to maintain and support than low-level\nJSON definitions.\n\n  * [Try / Catch](#try--catch-pattern)\n  * [Try / Finally](#try--finally-pattern)\n  * [Try / Catch / Finally](#try--catch--finally-pattern)\n  * [Retry with backoff and jitter](#retry-with-backoff-and-jitter)\n  * [Resilience lambda errors handling](#resilience-lambda-errors-handling)                                                                                                                                          \n  * [Validation of proper resilience lambda errors handling](#validation-of-proper-resilience-lambda-errors-handling)   \n  \n## Try / Catch pattern\nStep Functions support **Try / Catch** pattern natively with [Task](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-task-state.html)\nand [Parallel](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-parallel-state.html) states. \n\n`TryTask` construct adds a high level abstraction that allows you to use Try / Catch pattern with any state or sequence of states.\n\n### Example\n```typescript\nimport * as sfn from '@aws-cdk/aws-stepfunctions';\nimport { TryTask } from 'cdk-stepfunctions-patterns';\n\n// ...\n\nnew sfn.StateMachine(this, 'TryCatchStepMachine', {\n  definition: new TryTask(this, \"TryCatch\", {\n    tryProcess: new sfn.Pass(this, 'A1').next(new sfn.Pass(this, 'B1')),\n    catchProcess: new sfn.Pass(this, 'catchHandler'),\n    // optional configuration properties\n    catchProps: {\n      errors: ['Lambda.AWSLambdaException'],\n      resultPath: \"$.ErrorDetails\"\n    }\n  })\n})\n```\n\n### Resulting StepFunction\n![](doc/tryCatch.png)\n\n\n## Try / Finally pattern \nIt is often useful to design state machine using **Try / Finally** pattern. The idea is to have a *Final* state that has to be \nexecuted regardless of successful or failed execution of the *Try* state. There may be some temporal resource you want \nto delete or notification to send.\n\nStep Functions do not provide a native way to implement that pattern but it can be done using \n[Parallel](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-parallel-state.html) state and *catch all* catch\nspecification.\n\n`TryTask` construct abstracts these implementation details and allows to express the pattern directly.\n\n### Example\n\n```typescript\nimport * as sfn from '@aws-cdk/aws-stepfunctions';\nimport { TryTask } from 'cdk-stepfunctions-patterns';\n\n// ...\n\nnew sfn.StateMachine(this, 'TryFinallyStepMachine', {\n    definition: new TryTask(this, \"TryFinally\", {\n        tryProcess: new sfn.Pass(this, 'A2').next(new sfn.Pass(this, 'B2')),\n        finallyProcess: new sfn.Pass(this, 'finallyHandler'),\n        // optional configuration properties\n        finallyErrorPath: \"$.FinallyErrorDetails\"\n    })\n})\n```\n\n### Resulting StepFunction\n![](doc/tryFinally.png)\n\n## Try / Catch / Finally pattern\nThis is a combination of two previous patterns. `TryTask` construct allows you to express rather complex\nerror handling logic in a very compact form. \n\n### Example\n```typescript\nimport * as sfn from '@aws-cdk/aws-stepfunctions';\nimport { TryTask } from 'cdk-stepfunctions-patterns';\n\n// ...\n\nnew sfn.StateMachine(this, 'TryCatchFinallyStepMachine', {\n    definition: new TryTask(this, \"TryCatchFinalli\", {\n        tryProcess: new sfn.Pass(this, 'A3').next(new sfn.Pass(this, 'B3')),\n        catchProcess: new sfn.Pass(this, 'catchHandler3'),\n        finallyProcess: new sfn.Pass(this, 'finallyHandler3')\n    })\n})\n```\n\n### Resulting StepFunction\n![](doc/tryCatchFinally.png)\n\n## Retry with backoff and jitter\nOut of the box Step Functions retry implementation provides a way to configure backoff factor,\nbut there is no built in way to introduce jitter. As covered in \n[Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)\nand [Wait and Retry with Jittered Back-off](https://github.com/Polly-Contrib/Polly.Contrib.WaitAndRetry#wait-and-retry-with-jittered-back-off) this retry technique can be very helpful in high-load\nscenarios.\n\n`RetryWithJitterTask` construct provides a custom implementation of retry with backoff and \njitter that you can use directly in your state machines. \n\n### Example\n```typescript\nimport * as sfn from '@aws-cdk/aws-stepfunctions';\nimport { RetryWithJitterTask } from 'cdk-stepfunctions-patterns';\n\n// ...\n\nnew sfn.StateMachine(this, 'RetryWithJitterStepMachine', {\n    definition: new RetryWithJitterTask(this, \"AWithJitter\", {\n        tryProcess: new sfn.Pass(this, 'A4').next(new sfn.Pass(this, 'B4')),\n        retryProps: { errors: [\"States.ALL\"], maxAttempts: 3 }\n    })\n})\n```\n\n### Resulting StepFunction\n![](doc/retryWithJitter.png)\n\n## Resilience lambda errors handling\n`LambdaInvoke` construct from [aws-stepfunctions-tasks](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-stepfunctions-tasks-readme.html)\nmodule is probably one of the most used ones. Still, handling of \n[AWS Lambda service exceptions](https://docs.aws.amazon.com/step-functions/latest/dg/bp-lambda-serviceexception.html) \nis often overlooked. \n\n`ResilientLambdaTask` is a drop-in replacement construct for `LambdaInvoke` that adds retry for the most common \ntransient errors: \n\n - Lambda.ServiceException\n - Lambda.AWSLambdaException\n - Lambda.SdkClientException\n - Lambda.TooManyRequestsException\n\n ### Example\n```typescript\nimport * as lambda from '@aws-cdk/aws-lambda';\nimport { ResilientLambdaTask } from 'cdk-stepfunctions-patterns';\n\n// ...\n\nconst lambdaFunction = new lambda.Function(this, 'LambdaFunction', {\n    // ... removed for clarity\n});\n\nconst calculateJitterTask = new ResilientLambdaTask(this, \"InvokeLambda\", {\n    lambdaFunction: lambdaFunction  \n})\n```\n\nThat would result in the following state definition:\n\n```json\n\"InvokeLambda\": {\n    \"Type\": \"Task\",\n    \"Resource\": \"arn:aws:states:::lambda:invoke\",\n    \"Parameters\": {\n        \"FunctionName\": \"\u003cARN of lambda function\u003e\"\n    },\n    \"Retry\": [{\n        \"ErrorEquals\": [\n        \"Lambda.ServiceException\",\n        \"Lambda.AWSLambdaException\",\n        \"Lambda.SdkClientException\",\n        \"Lambda.TooManyRequestsException\"\n        ],\n        \"IntervalSeconds\": 2,\n        \"MaxAttempts\": 6,\n        \"BackoffRate\": 2\n    }]\n}\n```\n\n## Validation of proper resilience lambda errors handling\nIt is often a challenge to enforce consistent transient error handling across all state machines of a large\napplication. To help with that, *cdk-stepfuctions-patterns* provides a [CDK aspect](https://docs.aws.amazon.com/cdk/latest/guide/aspects.html)\nto verify that all Lambda invocations correctly handle transient errors from AWS Lambda service.\n\nUse `ResilienceLambdaChecker` aspect as shown below.\n\n ### Example\n```typescript\nimport * as cdk from '@aws-cdk/core';\nimport { ResilienceLambdaChecker } from 'cdk-stepfunctions-patterns'\n\nconst app = new cdk.App();\n// ...\n\n// validate compliance rules\napp.node.applyAspect(new ResilienceLambdaChecker());\n```\n\nIf there are some states in your application that do not retry transient errors or miss some recommended\nerror codes, there will be warning during CDK synthesize stage:\n\n```\nPS C:\\Dev\\GitHub\\cdk-stepfunctions-patterns\u003e cdk synth --strict\n[Warning at /StepFunctionsPatterns/A] No retry for AWS Lambda transient errors defined - consider using ResilientLambdaTask construct.\n[Warning at /StepFunctionsPatterns/B] Missing retry for transient errors: Lambda.AWSLambdaException,Lambda.SdkClientException.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkolomied%2Fcdk-stepfunctions-patterns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkolomied%2Fcdk-stepfunctions-patterns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkolomied%2Fcdk-stepfunctions-patterns/lists"}