4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS Step FunctionsのHello World テンプレートを読み解いて学ぶ

Last updated at Posted at 2024-12-28

AWS Step Functions

AWS Step Functionsというサービスが好きだったのですが最近触れておらず、設定や書き方を忘れてしまっていました。
久しぶりに触っていると、ステートマシンの作成時にHello Worldテンプレートが作成できるようになっており、気になって見ていると復習に最適な気がしたので今回コードを読み込んでみました。

スクリーンショット 2024-12-28 23.29.44.png

テンプレート定義全文

{
  "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.' %}"
      }
    }
  }
}

テンプレート全体像

stepfunctions_graph.png

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の場合

スクリーンショット 2024-12-28 21.47.37.png

Wait for X Secondsに変更した場合
スクリーンショット 2024-12-28 21.47.55.png

ステート

ここからが実際のステートになります

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.IsHelloWorldExampletrueだった場合、Wait for X Secondsステートに遷移
  • states.input.IsHelloWorldExamplefalseだった場合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に設定したExecutionWaitTimeInSecondsSecondsの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"
      }
    }
  ]

エラーが発生した場合→ErrorEqualsStates.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.' %}"

ステートマシンを実行してみる

スクリーンショット 2024-12-28 23.32.11.png

入力には何も指定せず、実行を開始を押下します。

ステートマシンが起動し、

最終的な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実行部分で先程よりも長く待機時間があり、

スクリーンショット 2024-12-28 22.54.56.png

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ステートに遷移してステートマシンが失敗に終わりました。

スクリーンショット 2024-12-28 23.12.16.png

以上、Step FunctionsのHello Worldテンプレートを読み込んでみました。

基本的なStateの使い方やOutput・エラーハンドリングなどを組み込んであるシンプルないいテンプレートで、学習に最適でした。

2024年秋にアップデートしたJSONata対応や変数の詳細については深く踏み込まなかったため、別でまた内容を整理したいと思います。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?