AWS Step Functionsで新たにサポートされたJSONataを使ってみた!
この記事は、ニフティグループ Advent Calendar 2024 23日目の記事です。
はじめに
こんにちは!ニフティライフスタイルで不動産サービスの開発をしているsaikeiです!
少し前までモバイルアプリの開発をメインで行っていたのですが、最近はAWSを用いたバックエンド側の開発にも携わっています。
今回は、先月AWSから発表された「Step FunctionsでのJSONataサポート」について実際に触る機会があったので、ご紹介したいと思います。
JSONataとは何か
JSONataとはJSONデータ用の軽量クエリおよび変換言語で、最小限の構文で洗練されたクエリ式が特徴です。(公式サイト)
データの操作や結合をするための演算子や、ユーザー定義関数を作成することもでき、結果を任意のJSON構造フォーマットで出力することができます。
例えば以下のようなJSONに対して、JSONataで $sum(Fruits.(price * quantity))
というクエリを実行すると「果物ごとに price × quantity
を計算し合計した値」 4200
が結果として出力されます。
// JSON
{
"Fruits" : [
{
"name": "apple",
"price": 100,
"quantity": 2
},
{
"name": "banana",
"price": 200,
"quantity": 5
},
{
"name": "orange",
"price": 300,
"quantity": 10
}
]
}
// JSONata
$sum(Fruits.(price * quantity))
-> 4200
JSONata公式で実際に試せる「JSONata Exerciser」というWebツールが用意されているので合わせてご確認ください。
実際にStep Functionsで使ってみる
今回実装する仕様
以下の処理を行うワークフローを作成していきます。
- 重複起動チェック
GetListExecutions
実行中のStep Functionsの数を取得CheckMultipleLaunches
自身を含めて2台以上起動している場合は処理を終了
- 未処理アイテムチェック
GetParameterStore
Parameter Storeから値を取得CheckUpdateUnprocessedItem
未処理アイテムが0の場合は処理を終了
- 処理を行う別のState Machineを起動
RunOtherStateMachine
Step Functionsを実行
※ 全てにおいてエラーが発生した場合は Fail
として終了させる
今までのJSONPathでの記述の場合、後続のステップに値を渡すためには、各ステップでOutputを記述するバケツリレーが必要でした。
しかし今回JSONataに対応したことで「変数」が使えるようになったため、以下のように変数に値を入れることで後続のすべてのステップから値を呼び出すことができます。
{
"selectedFruits": "apple",
"quantity": 30
}
詳しくは公式の「Processing input and output in Step Functions」をご覧ください。
ワークフロー定義
0. ベース定義
QueryLanguage
のデフォルトは JSONPath
のため、JSONata
を使うためには明示的に宣言する必要があります。
また「失敗/成功用」のステートもこの段階で用意しておきます。
QueryLanguage: JSONata
States:
Succeed:
Type: Succeed
Fail:
Type: Fail
1. 重複起動チェック
arn:aws:states:::aws-sdk:sfn:listExecutions
に引数で自身のStateMachineArnを渡すことで、結果を $states.result
からJSON形式で取得できます。
今回は実行数を取得したいため、$states.result.Executions
を $count()
で囲むことで「現在の実行数」を取得することができます。
これをJSONataで使えるようになった変数にassignすることで、後続のステップで値を呼び出すことができます。
GetListExecutions:
Type: Task
Arguments:
StateMachineArn: {自身のStateMachineArnを記載}
StatusFilter: RUNNING
Resource: arn:aws:states:::aws-sdk:sfn:listExecutions
Next: CheckMultipleLaunches
Assign:
runningStateMachineCount: >-
'{% $count($states.result.Executions) %}'
先ほどのステップにより $runningStateMachineCount
で実行数が取得できるようになったため、 Choice
を使って分岐を作成し「現在の実行数が2未満の場合」に後続の処理が実行されるようにします。
CheckMultipleLaunches:
Type: Choice
Choices:
- Next: GetParameterStore
Condition: '{% $runningStateMachineCount < 2 %}'
Default: Succeed
2. 未処理アイテムチェック
Resource
に arn:aws:states:::aws-sdk:ssm:getParameter
を指定し、引数として Name
にパラメーター名を渡すことでParameter Storeから値を取得することができます。
結果は $states.result
に格納されているため、先ほどと同様に新たな変数にassignすることで後続のステップで値を呼び出すことができます。
またParameter Storeからの取得に失敗した場合はエラーが投げられるので、 Catch
を定義することでエラー時の処理の記述ができます。
GetParameterStore:
Type: Task
Resource: arn:aws:states:::aws-sdk:ssm:getParameter
Arguments:
Name: /parameter/unprocessed_items_count
Assign:
unprocessedItemsCount: >-
'{% $states.result.Parameter.Value %}'
Next: CheckUpdateUnprocessedItem
Catch:
- ErrorEquals:
- States.ALL
Next: Fail
※ CheckUpdateUnprocessedItem
は 1. 重複起動チェック
の Choice
と同様の処理のため省略します
3. 処理を行う別のState Machineを起動
Resource
に arn:aws:states:::states:startExecution.sync:2
を指定し、引数として StateMachineArn
にStateMachineのarnを渡すことで該当のState Machineを起動することができます。
呼び出し先で処理が失敗した場合のエラーハンドリングは Catch
を用いることで指定することができます。(以下の例ではそのままFailとしています)
RunOtherStateMachine:
Type: Task
Resource: arn:aws:states:::states:startExecution.sync:2
Next: Succeed
Catch:
- ErrorEquals:
- States.ALL
Next: Fail
Arguments:
StateMachineArn: {別のStateMachineArnを記載}
ワークフローの結合
これまでに紹介したステップを繋げることで、冒頭に記載の仕様を満たすワークフローを作成することができます。コードの全容は長くなってしまうため記事の末尾に記載します。
さいごに
今回は Step Functions 定義を JSONata を用いて記述してみました。
私は初めての Step Functions 作成が JSONata だったので、JSONPath との比較はできませんが、コードを見る限りではとてもシンプルかつ分かりやすい形になったかなと思います。
また、今まではLambdaを経由しなければならなかった処理についても、JSONataを使うことで、ワークフローの一部として定義することができるようになったため、こちらも活用していきたいと思っています。
この記事が何か一つでもみなさんの参考になれば幸いです。
最後までお読みいただきありがとうございました!
おまけ:JSONataで定義したStep Functionsのワークフロー(全文)
QueryLanguage: JSONata
StartAt: GetListExecutions
States:
GetListExecutions:
Type: Task
Arguments:
StateMachineArn: {自身のStateMachineArnを記載}
StatusFilter: RUNNING
Resource: arn:aws:states:::aws-sdk:sfn:listExecutions
Next: CheckMultipleLaunches
Assign:
runningStateMachineCount: >-
'{% $count($states.result.Executions) %}'
CheckMultipleLaunches:
Type: Choice
Choices:
- Next: GetParameterStore
Condition: '{% $runningStateMachineCount < 2 %}'
Default: Succeed
GetParameterStore:
Type: Task
Resource: arn:aws:states:::aws-sdk:ssm:getParameter
Arguments:
Name: /parameter/unprocessed_items_count
Assign:
unprocessedItemsCount: >-
'{% $states.result.Parameter.Value %}'
Next: CheckUpdateUnprocessedItem
Catch:
- ErrorEquals:
- States.ALL
Next: Fail
CheckUpdateUnprocessedItem:
Type: Choice
Choices:
- Next: RunOtherStateMachine
Condition: '{% $unprocessedItemsCount > 0 %}'
Default: Succeed
RunOtherStateMachine:
Type: Task
Resource: arn:aws:states:::states:startExecution.sync:2
Next: Succeed
Catch:
- ErrorEquals:
- States.ALL
Next: Fail
Arguments:
StateMachineArn: {別のStateMachineArnを記載}
Succeed:
Type: Succeed
Fail:
Type: Fail
掲載内容は、記事執筆時点の情報をもとにしています。