
AWS Step FunctionsのMapステート内でエラーが起きても全体を停止しない方法
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。サービス部の武田です。
AWS Step Functionsはステートマシンとしてワークフローを定義し、効率よく実行できるサーバーレスオーケストレーションサービスです。Step FunctionsではMapという、入力配列を並行実行するためのステートが提供されています。
さてこのMapステートですが、普通に使用すると、あるブランチ(並行実行されている処理のうちのひとつ)がエラーなどで中断してしまうと、他のブランチも止まるという特徴があります。
例として次のようなステートマシンを定義してみます。
ASL(Amazon States Language)は次のようになっています。Passステートは次のMapステートに渡すための配列データを作っています。Mapステート内の処理は、値が3であれば3秒待機してFailステートで失敗する。それ以外であれば5秒待機してSuccessステートに遷移し成功する。という単純なものです。
{
"Comment": "A description of my state machine",
"StartAt": "Pass",
"States": {
"Pass": {
"Type": "Pass",
"Next": "Map",
"Result": {
"values": [
1,
2,
3,
4,
5
]
}
},
"Map": {
"Type": "Map",
"End": true,
"Iterator": {
"StartAt": "Choice",
"States": {
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$",
"NumericEquals": 3,
"Next": "3 Sec Wait"
}
],
"Default": "5 Sec Wait"
},
"3 Sec Wait": {
"Type": "Wait",
"Seconds": 3,
"Next": "Fail"
},
"Fail": {
"Type": "Fail"
},
"5 Sec Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "Success"
},
"Success": {
"Type": "Succeed"
}
}
},
"ItemsPath": "$.values"
}
}
}
実行してみると #2(=値が3) のブランチはFailステートに到達して失敗となっています。
一方で他のブランチはどうかというと、たとえば #1(=値が2) はキャンセルとなり、途中で処理が終わっています。
通常はこのように停止してしまった方がよいのですが、場合によってはあるブランチに左右されず、正常実行できるものは実行し切れた方がよい場合もあるでしょう。
途中でステートマシンを停止しないようにする
それでは先ほどのステートマシンを少し改変し、あるブランチのエラーで止まらないようにしてみましょう。改変後のステートマシンは次のようになります。
グラフだけではどう変わったのか分かりにくいですね。ASLは次のようになります。
{
"Comment": "A description of my state machine",
"StartAt": "Pass",
"States": {
"Pass": {
"Type": "Pass",
"Next": "Map",
"Result": {
"values": [
1,
2,
3,
4,
5
]
}
},
"Map": {
"Type": "Map",
"End": true,
"Iterator": {
"StartAt": "Parallel",
"States": {
"Parallel": {
"Type": "Parallel",
"Branches": [
{
"StartAt": "Choice",
"States": {
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$",
"NumericEquals": 3,
"Next": "3 Sec Wait"
}
],
"Default": "5 Sec Wait"
},
"3 Sec Wait": {
"Type": "Wait",
"Seconds": 3,
"Next": "Fail"
},
"Fail": {
"Type": "Fail"
},
"5 Sec Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "Success"
},
"Success": {
"Type": "Succeed"
}
}
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"Next": "Catch Success"
}
],
"End": true
},
"Catch Success": {
"Type": "Succeed"
}
}
},
"ItemsPath": "$.values"
}
}
}
ポイントは24行目のParallelステートおよび60行目のCatch定義です。Parallelステートは通常異なる処理を並行実行するためのステートですが、ここでは単なるコンテナ(入れ物)として使っています。CatchはこのParallelステートの中で何らかのエラーが起きた場合の例外処理を定義しています。ここではエラーが起きたら問答無用でSuccessステートになり処理を正常終了させています。
ではこのステートマシンを実行してみましょう。まずは先ほどステートマシンを停止する原因となっていた #2 です。Failステートに遷移して一度は失敗していますが、それがキャッチされ正常終了されています。
続いてキャンセルされていた #1 です。こちらは問題なくSuccessステートに遷移し正常実行できています。
まとめ
Mapステートのデフォルトの挙動では困る場面をエラーハンドリングを少し工夫することで回避できました。ただしエラーが起きた際の通知処理なども考慮すると、構成が複雑になることが懸念されます。その場合は無理にステートマシンの中で並列化するのではなく、そもそもステートマシンを複数回実行することなども検討するとよいでしょう。












