AWS Step Functions
AWS Step Functionsというサービスが好きだったのですが最近触れておらず、設定や書き方を忘れてしまっていました。
久しぶりに触っていると、ステートマシンの作成時にHello Worldテンプレートが作成できるようになっており、気になって見ていると復習に最適な気がしたので今回コードを読み込んでみました。
テンプレート定義全文
{
"Comment": "A Hello World example that demonstrates various state types in the Amazon States Language, and showcases data flow and transformations using variables and JSONata expressions. This example consists solely of flow control states, so no additional resources are needed to run it.",
"QueryLanguage": "JSONata",
"StartAt": "Set Variables and State Output",
"States": {
"Set Variables and State Output": {
"Type": "Pass",
"Comment": "A Pass state passes its input to its output, without performing work. They can also generate static JSON output, or transform JSON input using JSONata expressions, and pass the transformed data to the next state. Pass states are useful when constructing and debugging state machines.",
"Next": "Is Hello World Example?",
"Output": {
"IsHelloWorldExample": true,
"ExecutionWaitTimeInSeconds": 3
},
"Assign": {
"CheckpointCount": 0
}
},
"Is Hello World Example?": {
"Type": "Choice",
"Comment": "A Choice state adds branching logic to a state machine. Choice rules use the Condition property to evaluate expressions with custom JSONata logic, allowing for flexible branching.",
"Default": "Fail the Execution",
"Choices": [
{
"Next": "Wait for X Seconds",
"Condition": "{% $states.input.IsHelloWorldExample %}"
}
]
},
"Fail the Execution": {
"Type": "Fail",
"Comment": "A Fail state stops the execution of the state machine and marks it as a failure, unless it is caught by a Catch block.",
"Error": "Not a Hello World Example"
},
"Wait for X Seconds": {
"Type": "Wait",
"Comment": "A Wait state delays the state machine from continuing for a specified time.",
"Seconds": "{% $states.input.ExecutionWaitTimeInSeconds %}",
"Next": "Execute in Parallel",
"Assign": {
"CheckpointCount": "{% $CheckpointCount + 1 %}"
}
},
"Execute in Parallel": {
"Type": "Parallel",
"Comment": "A Parallel state can be used to create parallel branches of execution in your state machine.",
"Branches": [
{
"StartAt": "Format Execution Start Date",
"States": {
"Format Execution Start Date": {
"Type": "Pass",
"Output": {
"FormattedExecutionStartDate": "{% $fromMillis($toMillis($states.context.State.EnteredTime), '[M01]/[D01]') %}"
},
"End": true
}
}
},
{
"StartAt": "Snapshot Execution Elapsed Time",
"States": {
"Snapshot Execution Elapsed Time": {
"Type": "Pass",
"End": true,
"Output": {
"ElapsedTimeToSnapshot": "{% ($toMillis($now()) - $toMillis($states.context.Execution.StartTime)) / 1000 %}"
}
}
}
}
],
"Next": "Set Checkpoint",
"Catch": [
{
"ErrorEquals": [
"States.QueryEvaluationError"
],
"Next": "Set Checkpoint",
"Output": {
"ElapsedTimeToSnapshot": "Failed to calculate",
"FormattedExecutionStartDate": "Failed to format"
}
}
]
},
"Set Checkpoint": {
"Type": "Pass",
"Next": "Summarize the Execution",
"Assign": {
"CheckpointCount": "{% $CheckpointCount + 1 %}"
}
},
"Summarize the Execution": {
"Type": "Succeed",
"Comment": "A Succeed state indicates successful completion of the state machine.",
"Output": {
"Summary": "{% 'This Hello World execution began on ' & $states.input.FormattedExecutionStartDate & '. The state machine ran for ' & $states.input.ElapsedTimeToSnapshot & ' seconds before the snapshot was taken, passing through ' & $CheckpointCount & ' checkpoints, and has successfully completed.' %}"
}
}
}
}
テンプレート全体像
Topレベルの階層
Comment
ステートマシンに対するコメント
"Comment": "A Hello World example that demonstrates various state types in the Amazon States Language, and showcases data flow and transformations using variables and JSONata expressions. This example consists solely of flow control states, so no additional resources are needed to run it."
ステートマシン自体に対するコメントを設定します。
QueryLanguage
ステートマシンで使用するクエリ言語の設定
"QueryLanguage": "JSONata"
現在は2種類から選べるようになっており、このHello WorldテンプレートではJSONata
が採用されていることからも、AWS的にはJSONataを推奨しているようです。
- JSONPath・・・以前のデフォルト言語
- JSONata・・・新しく追加された言語
StartAt
ステート開始場所
"StartAt": "Set Variables and State Output"
ワークフローの開始点となる状態を示します。コンソールの設定ではプルダウンで開始点を選択できるようになっており、起点を変えると下記のようにStartの位置アイコンが変化します。
Set Variables and State Output
の場合
ステート
ここからが実際のステートになります
Set Variables and State Output
"Set Variables and State Output": {
"Type": "Pass",
"Comment": "A Pass state passes its input to its output, without performing work. They can also generate static JSON output, or transform JSON input using JSONata expressions, and pass the transformed data to the next state. Pass states are useful when constructing and debugging state machines.",
"Next": "Is Hello World Example?",
"Output": {
"IsHelloWorldExample": true,
"ExecutionWaitTimeInSeconds": 3
},
"Assign": {
"CheckpointCount": 0
}
}
Type Pass
"Type": "Pass"
Type
でステートの種類を設定します。Pass
や後述するChoice
等があります。
ステートの種類はPass
で、入力を出力に渡すシンプルなステートです。
Comment
"Comment": "A Pass state passes its input to its output, without performing work. They can also generate static JSON output, or transform JSON input using JSONata expressions, and pass the transformed data to the next state. Pass states are useful when constructing and debugging state machines."
このステートに対する説明を記載することができます。
Next
"Next": "Is Hello World Example?"
Nextに、このステートの次に遷移・実行させたいステートを設定します。
Output
"Output": {
"IsHelloWorldExample": true,
"ExecutionWaitTimeInSeconds": 3
}
Outputを使って、
"IsHelloWorldExample": true
-
"ExecutionWaitTimeInSeconds": 3
というKeyValueを設定し、後続のステートで使用できるようにしています。
Assign
"Assign": {
"CheckpointCount": 0
}
Assignは、2024年11月のアップデートによって登場した概念「変数」です。
参考
https://dev.classmethod.jp/articles/step-functions-variables/
CheckpointCount
というKeyに0
というValueを初期値として設定しています。
今後遷移した先のステートで扱われることになるので注目ポイントです。
Is Hello World Example?
*ここからは既出の設定・要素については省略します
"Is Hello World Example?": {
"Type": "Choice",
"Comment": "A Choice state adds branching logic to a state machine. Choice rules use the Condition property to evaluate expressions with custom JSONata logic, allowing for flexible branching.",
"Default": "Fail the Execution",
"Choices": [
{
"Next": "Wait for X Seconds",
"Condition": "{% $states.input.IsHelloWorldExample %}"
}
]
}
Type Choice
"Type": "Choice"
ステートの中で条件分岐ができるようになります。
Default
"Default": "Fail the Execution"
後述するChoices
のいずれの移行も実行されない場合の移行先の状態の名前を指定します。
Fail the Execution
のステートに遷移するよう設定されています。
Choices
"Choices": [
{
"Next": "Wait for X Seconds",
"Condition": "{% $states.input.IsHelloWorldExample %}"
}
]
Choices
に配列形式で次のステートの名称"Wait for X Seconds"
とそのステートに遷移する条件"Condition": "{% $states.input.IsHelloWorldExample %}"
を記載しています。
条件はCondition
をKeyにし、Value部分には"{% $states.input.IsHelloWorldExample %}"
でこのChoicesステートにinputとして渡された情報を参照している記述になっています。この
-
states.input.IsHelloWorldExample
がtrue
だった場合、Wait for X Seconds
ステートに遷移 -
states.input.IsHelloWorldExample
がfalse
だった場合DefaultのFail the Execution
に遷移
する設定になっています。
Fail the Execution
"Fail the Execution": {
"Type": "Fail",
"Comment": "A Fail state stops the execution of the state machine and marks it as a failure, unless it is caught by a Catch block.",
"Error": "Not a Hello World Example"
}
Type Fail
"Type": "Fail"
ステートマシンの実行を停止するステートです
Error
"Error": "Not a Hello World Example"
停止時のError内容をオプションとして定義できます
Wait for X Seconds
"Wait for X Seconds": {
"Type": "Wait",
"Comment": "A Wait state delays the state machine from continuing for a specified time.",
"Seconds": "{% $states.input.ExecutionWaitTimeInSeconds %}",
"Next": "Execute in Parallel",
"Assign": {
"CheckpointCount": "{% $CheckpointCount + 1 %}"
}
}
Type Wait
"Type": "Wait"
ステートマシンの続行を、指定された時間遅延させるステートです。
Seconds
"Seconds": "{% $states.input.ExecutionWaitTimeInSeconds %}"
Waitで待機する時間 (秒)を指定します。
ここではJSONataの読み込み方式でSet Variables and State Output
のステートでOutputに設定したExecutionWaitTimeInSeconds
をSeconds
のValueとして読み込んでいます。
Assign
"Assign": {
"CheckpointCount": "{% $CheckpointCount + 1 %}"
}
Set Variables and State Output
のステートで定義した変数CheckpointCount
を参照し、+1
インクリメントしています。このように、変数は途中のステートで参照・操作できるようになっています。
Execute in Parallel
"Execute in Parallel": {
"Type": "Parallel",
"Comment": "A Parallel state can be used to create parallel branches of execution in your state machine.",
"Branches": [
{
"StartAt": "Format Execution Start Date",
"States": {
"Format Execution Start Date": {
"Type": "Pass",
"Output": {
"FormattedExecutionStartDate": "{% $fromMillis($toMillis($states.context.State.EnteredTime), '[M01]/[D01]') %}"
},
"End": true
}
}
},
{
"StartAt": "Snapshot Execution Elapsed Time",
"States": {
"Snapshot Execution Elapsed Time": {
"Type": "Pass",
"End": true,
"Output": {
"ElapsedTimeToSnapshot": "{% ($toMillis($now()) - $toMillis($states.context.Execution.StartTime)) / 1000 %}"
}
}
}
}
],
"Next": "Set Checkpoint",
"Catch": [
{
"ErrorEquals": [
"States.QueryEvaluationError"
],
"Next": "Set Checkpoint",
"Output": {
"ElapsedTimeToSnapshot": "Failed to calculate",
"FormattedExecutionStartDate": "Failed to format"
}
}
]
}
Type Parallel
"Type": "Parallel"
並列に複数ステートを実行させる場合に使用します。Parallel内で設定したすべてのステートの実行が終了するまで、Parallelの次のステートには遷移しません。
Branches
"Branches": [
{
"StartAt": "Format Execution Start Date",
"States": {
"Format Execution Start Date": {
"Type": "Pass",
"Output": {
"FormattedExecutionStartDate": "{% $fromMillis($toMillis($states.context.State.EnteredTime), '[M01]/[D01]') %}"
},
"End": true
}
}
},
{
"StartAt": "Snapshot Execution Elapsed Time",
"States": {
"Snapshot Execution Elapsed Time": {
"Type": "Pass",
"End": true,
"Output": {
"ElapsedTimeToSnapshot": "{% ($toMillis($now()) - $toMillis($states.context.Execution.StartTime)) / 1000 %}"
}
}
}
}
]
typeがParallel
の場合の必須項目です。配列のオブジェクトに、並列に動かしたいステートを記述します。
states.context.State.EnteredTime
・・・ステートマシンの実行が開始された時刻を参照します
それぞれのステートで下記の処理を行ってOutputの出力にしています。
- (states.context.State.EnteredTime)をミリ秒に変換し、"MM/DD"形式の文字列に変換して、この変数に格納
- 現在時刻から実行開始時刻を差し引き、ミリ秒から秒に変換して、ここまでの経過時間を計算し、変数に格納
End
Next
で次のステートを明示的に指定せず、そのステートで終了させたい場合に、"End": true
を指定します。
Catch
"Catch": [
{
"ErrorEquals": [
"States.QueryEvaluationError"
],
"Next": "Set Checkpoint",
"Output": {
"ElapsedTimeToSnapshot": "Failed to calculate",
"FormattedExecutionStartDate": "Failed to format"
}
}
]
エラーが発生した場合→ErrorEquals
がStates.QueryEvaluationError
だった場合にNext
で定義したステート(ここではSet Checkpoint
)に遷移させる設定です。
Set Checkpoint
"Set Checkpoint": {
"Type": "Pass",
"Next": "Summarize the Execution",
"Assign": {
"CheckpointCount": "{% $CheckpointCount + 1 %}"
}
}
Assign
変数CheckpointCount
を1インクリメントしています。
Summarize the Execution
"Summarize the Execution": {
"Type": "Succeed",
"Comment": "A Succeed state indicates successful completion of the state machine.",
"Output": {
"Summary": "{% 'This Hello World execution began on ' & $states.input.FormattedExecutionStartDate & '. The state machine ran for ' & $states.input.ElapsedTimeToSnapshot & ' seconds before the snapshot was taken, passing through ' & $CheckpointCount & ' checkpoints, and has successfully completed.' %}"
}
}
Type Succeed
Succeed
という成功の状態の定義で、ここに到達した場合、ステートマシンが成功とみなされます。
(逆にFail
に到達した場合はステートマシンが失敗とみなされます。)
Output
ステートマシンの実行結果を出力します
JSOnataの埋め込み式を使っています
"{% 'This Hello World execution began on ' & $states.input.FormattedExecutionStartDate & '. The state machine ran for ' & $states.input.ElapsedTimeToSnapshot & ' seconds before the snapshot was taken, passing through ' & $CheckpointCount & ' checkpoints, and has successfully completed.' %}"
ステートマシンを実行してみる
入力には何も指定せず、実行を開始
を押下します。
ステートマシンが起動し、
最終的なoutputの出力はこちらです。
{
"output": {
"Summary": "This Hello World execution began on 12/28. The state machine ran for 3.094 seconds before the snapshot was taken, passing through 12 checkpoints, and has successfully completed."
},
"outputDetails": {
"truncated": false
}
}
- 実行された日付
- 実行時間
- チェックポイントを通過した数
がSummaryに出力されていることが分かります。
数値の検証
Set Variables and State Output
で定義されている設定値を変えて実行してみます。
"ExecutionWaitTimeInSeconds": 3
を"ExecutionWaitTimeInSeconds": 10
に変えて実行します。
{
"IsHelloWorldExample": true,
- "ExecutionWaitTimeInSeconds": 3
+ "ExecutionWaitTimeInSeconds": 10
}
Wait For X seconds実行部分で先程よりも長く待機時間があり、
The state machine ran for 10.096 seconds
部分も変化していることが分かります。
{
"output": {
"Summary": "This Hello World execution began on 12/28. The state machine ran for 10.096 seconds before the snapshot was taken, passing through 12 checkpoints, and has successfully completed."
},
"outputDetails": {
"truncated": false
}
}
Failの検証
Set Variables and State Output
で定義されている設定値を変えて実行してみます。
{
- "IsHelloWorldExample": true,
+ "IsHelloWorldExample": false,
"ExecutionWaitTimeInSeconds": 3
}
に変更して実行してみます。すると、期待通りFailステートに遷移してステートマシンが失敗に終わりました。
以上、Step FunctionsのHello Worldテンプレートを読み込んでみました。
基本的なStateの使い方やOutput・エラーハンドリングなどを組み込んであるシンプルないいテンプレートで、学習に最適でした。
2024年秋にアップデートしたJSONata対応や変数の詳細については深く踏み込まなかったため、別でまた内容を整理したいと思います。