[アップデート] AWS Step Functionsのステートマシン実行失敗時点からの再実行をサポートしました
ステートマシンの途中から再実行したい
こんにちは、のんピ(@non____97)です。
皆さんはAWS Step Functionsのステートマシンの実行が失敗した時に、失敗したステートから再実行したいなと思ったことはありますか? 私はあります。
今までステートマシンが途中で終了した際に再実行する場合、ワークフローの最初から実行する必要がありました。
ステートマシンが実行失敗した際にワークフローの最初から実行する場合、正常に実行完了していた処理をもう一度実行することとなってしまいます。そのため、ワークフローは最初から実行し直すことを考慮して設計する必要がありました。
要するに冪等性が求められています。
ステートマシンにおいて冪等性を持たせたい場合は以下のような方法があります。
- 再実行時に実行済みのステートをスキップする
- エラーが発生した際に、途中の変更内容を削除して、ワークフローの開始時の状態にロールバックさせる
サーバーレスにおける冪等性の実装はbuilders.flashの以下シリーズが非常に参考になります。
冪等性を持たせるのは中々大変です。JP1やSystemwalkerなどのジョブ管理システムを使われたことがある方は、「ジョブネットみたいに途中から動かしたい...」ともどかしい思いをしたことがあるのではないでしょうか。
再実行時のスコープを狭くするため、ステートマシンを細かく分割された方もいるかと思います。
今回、アップデートでAWS Step Functionsがステートマシン実行失敗時点からの再実行をサポートしました。
AWS Blogsでも紹介されていますね。ステートマシン実行失敗時点からの再実行をRedrive
と呼ぶようです。
AWS公式ドキュメントも充実していますね。
これにより、頑張って冪等性のあるワークフローを組む場面が大幅に減りそうです。また、ステートマシンの課金要素には状態遷移の数があるため、再実行時のコストを削減することにも繋がります。
実際に触ってみて、使い勝手を確認してみました。
いきなりまとめ
- ステートマシン実行失敗時点からの再実行は
Redrive
と呼ぶ - APIはRedriveExecution
- Redriveで実行するステートは選択できない
- 必ず失敗したステートから実行することとなる
- そのため、成功した実行のRedriveはできない
- Redrive時にステートマシンのInputを変更することはできない
- ワークフローを編集して、編集したワークフローでRedriveすることもできない
- EventBridgeに発行される
Step Functions Execution Status Change
のスキーマが変更された- Redriveの回数や状態などが追加
- Redriveで対応できない場合に備えて冪等性のあるワークフローを組んだり、リトライ処理を組み込むのは引き続き重要
やってみる
Redrive時にステートマシンのInputやワークフローの定義を変更することはできない件
以下のようにステートマシンのInputで渡された名前のLambda関数を呼び出すステートマシンを用意しました。
こちらのステートマシンを実行します。ステートマシンのInputにはあえてLambda関数の名前を指定しないでおきます。
ステートマシンの実行に失敗しました。呼び出すべきLambda関数の指定がないので当然です。
ステートマシンをRedriveするためにRecover
-Redrive from faillure
をクリックします。
Redriveの確認画面は以下のとおりです。
Inputを変更しようと思っていましたが、どうやらできないようです。
専用のAPIとしてRedriveExecutionが生えていました。
こちらのAPIを確認すると、Redriveでは失敗時と同じInputやステートマシンの定義、実行ARNが使用されると記載されていました。
Restarts unsuccessful executions of Standard workflows that didn't complete successfully in the last 14 days. These include failed, aborted, or timed out executions. When you redrive an execution, it continues the failed execution from the unsuccessful step and uses the same input. Step Functions preserves the results and execution history of the successful steps, and doesn't rerun these steps when you redrive an execution. Redriven executions use the same state machine definition and execution ARN as the original execution attempt.
要するに、ステートマシンのInputが原因でステートマシンが失敗した場合のRedriveは解決策になり得ません。また、失敗箇所を修正するようにワークフローを編集しても、編集したワークフローでRedriveすることもできません。
今回の場合、こうなってしまうとどうしようもありません。ステートマシンのInputを変更するために最初から実行することになります。
試しにRedriveしてみます。
はい、失敗に失敗を重ねただけです。ただし、失敗したLambda関数の呼び出しから実行していることは確認できました。
Redriveの成功パターンの確認
気を取り直してRedriveの成功パターンを確認します。
ステートマシンのIAMロールにアタッチされているIAMポリシーにて、以下のようにLambda関数の呼び出しができないようにしてあげます。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:us-east-1:<AWSアカウントID>:function:*" ] } ] }
この状態でステートマシンを実行します。
権限不足により失敗していますね。
IAMポリシーを修正して、Lambda関数を呼び出せるようにします。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:us-east-1:<AWSアカウントID>:function:*" ] } ] }
この状態でRedriveします。
すると、今度は正常に実行が完了しました。Redrive最高です。
EventBridgeに発行されるイベントの確認
Redriveの機能が追加されたにあたってEventBridgeに発行されるイベントスキーマが変更されています。
ステートマシン実行失敗時のStep Functions Execution Status Change
を確認します。
{ "version": "0", "id": "10887a14-cee9-1902-2b23-2cb76b7a2f0f", "detail-type": "Step Functions Execution Status Change", "source": "aws.states", "account": "<AWSアカウントID>", "time": "2023-11-16T06:38:03Z", "region": "us-east-1", "resources": [ "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5" ], "detail": { "executionArn": "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "stateMachineArn": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:MyStateMachine-gddoyum7f", "name": "0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "status": "FAILED", "startDate": 1700116674994, "stopDate": 1700116682923, "input": "{\n \"FunctionName\": \"test\"\n}", "output": null, "stateMachineVersionArn": null, "stateMachineAliasArn": null, "redriveCount": 0, "redriveDate": null, "redriveStatus": "REDRIVABLE", "redriveStatusReason": null, "inputDetails": { "included": true }, "outputDetails": null, "error": "Lambda.AWSLambdaException", "cause": "User: arn:aws:sts::<AWSアカウントID>:assumed-role/StepFunctions-MyStateMachine-gddoyum7f-role-4e4co5vgy/peFCootmIyFjdbshoxqJHoBZMjcgLDCv is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:<AWSアカウントID>:function:test with an explicit deny in an identity-based policy (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: f14c8058-6987-4603-a9a2-2dc036844257; Proxy: null)" } }
以下のようにRedrive関連の4つのフィールドが追加されていることが確認できます。
redriveCount
: Redriveの回数redriveDate
: Redriveした時間redriveStatus
: Redriveが可能かどうかのステータスredriveStatusReason
: Redriveができない場合の原因
redriveStatus
やredriveStatusReason
の値とその説明はDescribeExecutionのAPI Referenceに記載ありました。
redriveStatus
Indicates whether or not an execution can be redriven at a given point in time.
- For executions of type STANDARD, redriveStatus is NOT_REDRIVABLE if calling the RedriveExecution API action would return the ExecutionNotRedrivable error.
- For a Distributed Map that includes child workflows of type STANDARD, redriveStatus indicates whether or not the Map Run can redrive child workflow executions.
- For a Distributed Map that includes child workflows of type EXPRESS, redriveStatus indicates whether or not the Map Run can redrive child workflow executions.
You can redrive failed or timed out EXPRESS workflows only if they're a part of a Map Run. When you redrive the Map Run, these workflows are restarted using the StartExecution API action.
Type: String
Valid Values: REDRIVABLE | NOT_REDRIVABLE | REDRIVABLE_BY_MAP_RUN
redriveStatusReason
When redriveStatus is NOT_REDRIVABLE, redriveStatusReason specifies the reason why an execution cannot be redriven.
For executions of type STANDARD, or for a Distributed Map that includes child workflows of type STANDARD, redriveStatusReason can include one of the following reasons:
State machine is in DELETING status.
- Execution is RUNNING and cannot be redriven.
- Execution is SUCCEEDED and cannot be redriven.
- Execution was started before the launch of RedriveExecution.
- Execution history event limit exceeded.
- Execution has exceeded the max execution time.
- Execution redrivable period exceeded.
For a Distributed Map that includes child workflows of type EXPRESS, redriveStatusReason is only returned if the child workflows are not redrivable. This happens when the child workflow executions have completed successfully.
Type: String
Length Constraints: Maximum length of 262144.
Redriveしたことによるイベントを眺めてみます。
{ "version": "0", "id": "f5d42225-8653-04b9-c835-53923f3949d6", "detail-type": "Step Functions Execution Status Change", "source": "aws.states", "account": "<AWSアカウントID>", "time": "2023-11-16T06:39:04Z", "region": "us-east-1", "resources": [ "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5" ], "detail": { "executionArn": "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "stateMachineArn": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:MyStateMachine-gddoyum7f", "name": "0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "status": "RUNNING", "startDate": 1700116674994, "stopDate": null, "input": "{\n \"FunctionName\": \"test\"\n}", "output": null, "stateMachineVersionArn": null, "stateMachineAliasArn": null, "redriveCount": 1, "redriveDate": 1700116743721, "redriveStatus": "NOT_REDRIVABLE", "redriveStatusReason": "Execution is RUNNING and cannot be redriven", "inputDetails": { "included": true }, "outputDetails": null, "error": null, "cause": null } }
{ "version": "0", "id": "a1625904-11f7-900d-6c34-18375fcff8d2", "detail-type": "Step Functions Execution Status Change", "source": "aws.states", "account": "<AWSアカウントID>", "time": "2023-11-16T06:39:11Z", "region": "us-east-1", "resources": [ "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5" ], "detail": { "executionArn": "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "stateMachineArn": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:MyStateMachine-gddoyum7f", "name": "0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "status": "FAILED", "startDate": 1700116674994, "stopDate": 1700116751634, "input": "{\n \"FunctionName\": \"test\"\n}", "output": null, "stateMachineVersionArn": null, "stateMachineAliasArn": null, "redriveCount": 1, "redriveDate": 1700116743721, "redriveStatus": "REDRIVABLE", "redriveStatusReason": null, "inputDetails": { "included": true }, "outputDetails": null, "error": "Lambda.AWSLambdaException", "cause": "User: arn:aws:sts::<AWSアカウントID>:assumed-role/StepFunctions-MyStateMachine-gddoyum7f-role-4e4co5vgy/JRgakUQNuRkmbVGLfzMzphFVJCiqkSjU is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:<AWSアカウントID>:function:test with an explicit deny in an identity-based policy (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: 81212526-0091-4829-8326-68a35f30577c; Proxy: null)" } }
{ "version": "0", "id": "b3a4d59a-31ac-4e6c-e1f3-4abe0bba7879", "detail-type": "Step Functions Execution Status Change", "source": "aws.states", "account": "<AWSアカウントID>", "time": "2023-11-16T06:40:56Z", "region": "us-east-1", "resources": [ "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5" ], "detail": { "executionArn": "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "stateMachineArn": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:MyStateMachine-gddoyum7f", "name": "0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "status": "FAILED", "startDate": 1700116674994, "stopDate": 1700116856509, "input": "{\n \"FunctionName\": \"test\"\n}", "output": null, "stateMachineVersionArn": null, "stateMachineAliasArn": null, "redriveCount": 2, "redriveDate": 1700116848681, "redriveStatus": "REDRIVABLE", "redriveStatusReason": null, "inputDetails": { "included": true }, "outputDetails": null, "error": "Lambda.AWSLambdaException", "cause": "User: arn:aws:sts::<AWSアカウントID>:assumed-role/StepFunctions-MyStateMachine-gddoyum7f-role-4e4co5vgy/lMgkdCqJMGkRfLZnfYQVlhhCKODCNaDD is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:<AWSアカウントID>:function:test with an explicit deny in an identity-based policy (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: 487bf582-66f7-4f63-85fe-554a5e19822f; Proxy: null)" } }
{ "version": "0", "id": "2751ad77-efac-351f-1c2f-88f95f7db560", "detail-type": "Step Functions Execution Status Change", "source": "aws.states", "account": "<AWSアカウントID>", "time": "2023-11-16T06:42:29Z", "region": "us-east-1", "resources": [ "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5" ], "detail": { "executionArn": "arn:aws:states:us-east-1:<AWSアカウントID>:execution:MyStateMachine-gddoyum7f:0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "stateMachineArn": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:MyStateMachine-gddoyum7f", "name": "0e3a7824-7ec2-4ced-9aef-61f71fbef1b5", "status": "SUCCEEDED", "startDate": 1700116674994, "stopDate": 1700116949150, "input": "{\n \"FunctionName\": \"test\"\n}", "output": "{\"statusCode\":200,\"body\":\"\\\"Hello from Lambda!\\\"\"}", "stateMachineVersionArn": null, "stateMachineAliasArn": null, "redriveCount": 3, "redriveDate": 1700116948644, "redriveStatus": "NOT_REDRIVABLE", "redriveStatusReason": "Execution is SUCCEEDED and cannot be redriven", "inputDetails": { "included": true }, "outputDetails": { "included": true }, "error": null, "cause": null } }
redriveCount
がカウントされていますね。
「特定の回数までは自動でRedriveを繰り返す」みたいなEventBridge Ruleも組めそうです。
冪等性のあるワークフローを組んだり、リトライ処理を組み込むのは引き続き重要
AWS Step Functionsがステートマシン実行失敗時点からの再実行をサポートしたアップデートを紹介しました。
Redriveを用いることで、リトライ処理では対応しきれない長期間の外的要因による中断時の再実行がかなりしやすくなりました。
また、ワークフローの最初から実行する必要がなくなったため、トラブルシューティングの時間も短くなりそうですね。
なお、紹介しているとおり、Redriveで対応できない場面もあります。そのような場面に備えて、冪等性のあるワークフローを組んだり、リトライ処理を組み込むのは引き続き重要だと考えます。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!