Skip to content

Commit ca2ca27

Browse files
author
Kolomiets
committed
Add documentation about the patterns / constructs provided
1 parent ea43aa2 commit ca2ca27

File tree

1 file changed

+118
-10
lines changed

1 file changed

+118
-10
lines changed

README.md

Lines changed: 118 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010
*cdk-stepfunctions-patterns* library is a set of [AWS CDK](https://aws.amazon.com/cdk/) constructs that provide
1111
resiliency patterns implementation for AWS Step Functions.
1212

13+
All these patterns are *composable*, meaning that you can combine them together to create
14+
quite complex state machines that are much easier to maintain and support than low-level
15+
JSON definitions.
16+
1317
## Try / Catch pattern
18+
Step Functions support **Try / Catch** pattern natively with [Task](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-task-state.html)
19+
and [Parallel](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-parallel-state.html) states.
20+
21+
`TryTask` construct adds a high level abstraction that allows you to use Try / Catch pattern with any state or sequence of states.
1422

1523
### Example
1624
```typescript
@@ -37,6 +45,15 @@ new sfn.StateMachine(this, 'TryCatchStepMachine', {
3745

3846

3947
## Try / Finally pattern
48+
It is often useful to design state machine using **Try / Finally** pattern. The idea is to have a *Final* state that has to be
49+
executed regardless of successful or failed execution of the *Try* state. There may be some temporal resource you want
50+
to delete or notification to send.
51+
52+
Step Functions do not provide a native way to implement that pattern but it can be done using
53+
[Parallel](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-parallel-state.html) state and *catch all* catch
54+
specification.
55+
56+
`TryTask` construct abstracts these implementation details and allows to express the pattern directly.
4057

4158
### Example
4259

@@ -48,10 +65,10 @@ import { TryTask } from 'cdk-stepfunctions-patterns';
4865

4966
new sfn.StateMachine(this, 'TryFinallyStepMachine', {
5067
definition: new TryTask(this, "TryFinally", {
51-
tryProcess: new sfn.Pass(this, 'A2').next(new sfn.Pass(this, 'B2')),
52-
finallyProcess: new sfn.Pass(this, 'finallyHandler'),
53-
// optional configuration properties
54-
catchErrorPath: "$.FinallyErrorDetails"
68+
tryProcess: new sfn.Pass(this, 'A2').next(new sfn.Pass(this, 'B2')),
69+
finallyProcess: new sfn.Pass(this, 'finallyHandler'),
70+
// optional configuration properties
71+
finallyErrorPath: "$.FinallyErrorDetails"
5572
})
5673
})
5774
```
@@ -60,6 +77,8 @@ new sfn.StateMachine(this, 'TryFinallyStepMachine', {
6077
![](doc/tryFinally.png)
6178

6279
## Try / Catch / Finally pattern
80+
This is a combination of two previous patterns. `TryTask` construct allows you to express rather complex
81+
error handling logic in a very compact form.
6382

6483
### Example
6584
```typescript
@@ -70,9 +89,9 @@ import { TryTask } from 'cdk-stepfunctions-patterns';
7089

7190
new sfn.StateMachine(this, 'TryCatchFinallyStepMachine', {
7291
definition: new TryTask(this, "TryCatchFinalli", {
73-
tryProcess: new sfn.Pass(this, 'A3').next(new sfn.Pass(this, 'B3')),
74-
catchProcess: new sfn.Pass(this, 'catchHandler3'),
75-
finallyProcess: new sfn.Pass(this, 'finallyHandler3')
92+
tryProcess: new sfn.Pass(this, 'A3').next(new sfn.Pass(this, 'B3')),
93+
catchProcess: new sfn.Pass(this, 'catchHandler3'),
94+
finallyProcess: new sfn.Pass(this, 'finallyHandler3')
7695
})
7796
})
7897
```
@@ -81,6 +100,14 @@ new sfn.StateMachine(this, 'TryCatchFinallyStepMachine', {
81100
![](doc/tryCatchFinally.png)
82101

83102
## Retry with backoff and jitter
103+
Out of the box Step Functions retry implementation provides a way to configure backoff factor,
104+
but there is no built in way to introduce jitter. As covered in
105+
[Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)
106+
and [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
107+
scenarios.
108+
109+
`RetryWithJitterTask` construct provides a custom implementation of retry with backoff and
110+
jitter that you can use directly in your state machines.
84111

85112
### Example
86113
```typescript
@@ -91,11 +118,92 @@ import { RetryWithJitterTask } from 'cdk-stepfunctions-patterns';
91118

92119
new sfn.StateMachine(this, 'RetryWithJitterStepMachine', {
93120
definition: new RetryWithJitterTask(this, "AWithJitter", {
94-
tryProcess: new sfn.Pass(this, 'A4').next(new sfn.Pass(this, 'B4')),
95-
retryProps: { errors: ["States.ALL"], maxAttempts: 3 }
121+
tryProcess: new sfn.Pass(this, 'A4').next(new sfn.Pass(this, 'B4')),
122+
retryProps: { errors: ["States.ALL"], maxAttempts: 3 }
96123
})
97124
})
98125
```
99126

100127
### Resulting StepFunction
101-
![](doc/retryWithJitter.png)
128+
![](doc/retryWithJitter.png)
129+
130+
## Resilience lambda errors handling
131+
`LambdaInvoke` construct from [aws-stepfunctions-tasks](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-stepfunctions-tasks-readme.html)
132+
module is probably one of the most used ones. Still, handling of
133+
[AWS Lambda service exceptions](https://docs.aws.amazon.com/step-functions/latest/dg/bp-lambda-serviceexception.html)
134+
is often overlooked.
135+
136+
`ResilientLambdaTask` is a drop-in replacement construct for `LambdaInvoke` that adds retry for the most common
137+
transient errors:
138+
139+
- Lambda.ServiceException
140+
- Lambda.AWSLambdaException
141+
- Lambda.SdkClientException
142+
- Lambda.TooManyRequestsException
143+
144+
### Example
145+
```typescript
146+
import * as lambda from '@aws-cdk/aws-lambda';
147+
import { ResilientLambdaTask } from 'cdk-stepfunctions-patterns';
148+
149+
// ...
150+
151+
const lambdaFunction = new lambda.Function(this, 'LambdaFunction', {
152+
// ... removed for clarity
153+
});
154+
155+
const calculateJitterTask = new ResilientLambdaTask(this, "InvokeLambda", {
156+
lambdaFunction: lambdaFunction
157+
})
158+
```
159+
160+
That would result in the following state definition:
161+
162+
```json
163+
"InvokeLambda": {
164+
"Type": "Task",
165+
"Resource": "arn:aws:states:::lambda:invoke",
166+
"Parameters": {
167+
"FunctionName": "<ARN of lambda function>"
168+
},
169+
"Retry": [{
170+
"ErrorEquals": [
171+
"Lambda.ServiceException",
172+
"Lambda.AWSLambdaException",
173+
"Lambda.SdkClientException",
174+
"Lambda.TooManyRequestsException"
175+
],
176+
"IntervalSeconds": 2,
177+
"MaxAttempts": 6,
178+
"BackoffRate": 2
179+
}]
180+
}
181+
```
182+
183+
## Validation of proper resilience lambda errors handling
184+
It is often a challenge to enforce consistent transient error handling across all state machines of a large
185+
application. To help with that, *cdk-stepfuctions-patterns* provides a [CDK aspect](https://docs.aws.amazon.com/cdk/latest/guide/aspects.html)
186+
to verify that all Lambda invocations correctly handle transient errors from AWS Lambda service.
187+
188+
Use `ResilienceLambdaChecker` aspect as shown below.
189+
190+
### Example
191+
```typescript
192+
import * as cdk from '@aws-cdk/core';
193+
import { ResilienceLambdaChecker } from 'cdk-stepfunctions-patterns'
194+
195+
const app = new cdk.App();
196+
// ...
197+
198+
// validate compliance rules
199+
app.node.applyAspect(new ResilienceLambdaChecker());
200+
```
201+
202+
If there are some states in your application that do not retry transient errors or miss some recommended
203+
error codes, there will be warning during CDK synthesize stage:
204+
205+
```
206+
PS C:\Dev\GitHub\cdk-stepfunctions-patterns> cdk synth --strict
207+
[Warning at /StepFunctionsPatterns/A] No retry for AWS Lambda transient errors defined - consider using ResilientLambdaTask construct.
208+
[Warning at /StepFunctionsPatterns/B] Missing retry for transient errors: Lambda.AWSLambdaException,Lambda.SdkClientException.
209+
```

0 commit comments

Comments
 (0)