StepFunction の機能のみでループ処理を作成する
はじめに
業務の中で StepFunction を用いてループ処理を組む機会がありましたので、備忘録として残そうと思います。
作成したもの
-
ワークフロー図
-
Json コード
{ "Comment": "sample", "StartAt": "DefineVariables", "States": { "DefineVariables": { "Next": "ChoiceRetryCount", "Parameters": { "RetryCount": 1 }, "Type": "Pass" }, "ChoiceRetryCount": { "Choices": [ { "Next": "DecrementRetryCount", "NumericGreaterThanEquals": 1, "Variable": "$.RetryCount" } ], "Default": "SuccessState", "Type": "Choice" }, "DecrementRetryCount": { "Next": "ChoiceRetryCount", "Parameters": { "RetryCount.$": "States.MathAdd($.RetryCount, -1)" }, "Type": "Pass" }, "SuccessState": { "Type": "Succeed" } } }
解説
StepFunction の State
に定義しているものを上から解説していきます。
DefineVariables
"DefineVariables": {
"Next": "ChoiceRetryCount",
"Parameters": {
"RetryCount": 1
},
"Type": "Pass"
}
StartAt
に定義している最初に実施される State であり、可読性を重視して StepFunction の先頭で RetryCount
を定義しています。
RetryCount
に 1 を設定すると、1 度のみループ処理を行うことになります。
また、Parameters
の RetryCount
を以下のように設定することで、StepFunction の実行時にリトライ回数のパラメータを渡すことも可能です。
"Parameters": {
"RetryCount.$": "$.RetryCount"
}
ChoiceRetryCount
"ChoiceRetryCount": {
"Choices": [
{
"Next": "DecrementRetryCount",
"NumericGreaterThanEquals": 1,
"Variable": "$.RetryCount"
}
],
"Default": "SuccessState",
"Type": "Choice"
}
StepFunction の if 文である Choice State を利用して RetryCount
が 1 より多い場合は RetryCount の減算処理へ、RetryCount
が 0 の場合には処理を抜けています。
通常のループ処理であればリトライの最大回数等を定義して利用すると思いますが、私は変数が多くなると可読性が悪くなると感じたため、この StepFunction のループ処理の中では RetryCount
のみを変数として利用しています。
DecrementRetryCount
"DecrementRetryCount": {
"Next": "ChoiceRetryCount",
"Parameters": {
"RetryCount.$": "States.MathAdd($.RetryCount, -1)"
},
"Type": "Pass"
}
ここでは、StepFunction の組み込み関数である States.MathAdd
を用いて RetryCount
から 1 を引く減算処理を行っています。
重ねてになりますが、ここで RetryCount
が 0 になった場合には ChoiceRetryCount
で処理を抜けることになっています。
応用
StepFunction でループ処理だけをする人はいないと思うので、基本的にはDefineVariables
の後にループしたい処理を入れることになります。
例として、Fargate の実行失敗の際にループ処理を行うようにします。
"DefineVariables": {
"Next": "RunFargateTask",
"Parameters": {
"RetryCount": 1
},
"Type": "Pass"
},
// RunFargateTask を追加
"RunFargateTask": {
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"Next": "ChoiceRetryCount",
"ResultPath": "$.FailedResult"
}
],
"Next": "SuccessState",
"Parameters": {
...
省略
...
},
"Resource": "arn:aws:states:::ecs:runTask.sync",
"Type": "Task"
}
この時、ループしたい処理の出力結果を ResultPath
に必ず設定する必要があります。
ResultPath
を設定しない場合には、DefineVariables
で定義した RetryCount
が以降の処理で使えなくなるため注意してください。
なお、この仕様は State 間で変数を受け渡す場合に重要になるため、是非 StepFunction をテストしつつ実際の挙動を確かめていただければと思います。
おわりに
ループ処理を作成したきっかけは、応用の章で記載した Fargate の起動失敗時のリトライ処理を自前で組む必要があったためですが、当初は Lambda 関数を利用する必要があると思っていました。しかし、調べていくと Fargate の実行結果を取得するための States.StringToJson
なども活用して StepFunction のみでリトライ処理を組むことができ、便利さに驚かされました。
このほかにも StepFunction では、直接 AWS SDK の機能も利用できるようなので、今後も積極的に利用していきたいと思っています。
Discussion