AWS Step Functionsの実装で引っかかったポイント
はじめまして!エンジニア2年目の Kseisaimin です。
この記事では、AWS Step Functions と AWS Lambda を組み合わせた実装を行った際にエラー調査や原因解析に時間がかかったポイントをまとめました。
自身の環境情報
OS: Windows 11
リージョン:ap-northeast-1 (東京)
引っかかったポイント①:JSONPathとJSONataの設定ミス
Step Functions を実装する際、AI が生成した JSON コードをそのまま用いて定義を作成したところ、JSON の形式が不正で実行できないという問題が発生しました。
調査した結果、Step Functions では利用可能な JSON の書式が複数存在し、その指定が適切でなかったことが原因であると分かりました。
Step Functions は 2024年11月に JSONata を正式サポートし、既存の JSONPath に代わるクエリ言語として位置付けられています(参照)。
JSONata と JSONPath では、例えば次のような書式の違いがあります。
ここでは例として、Payload 内の変数名をリネームして Lambda に渡すケースを見てみましょう。
JSONPath(Parameters を使った Lambda 呼び出しの例):
{
"CallLambda": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
"Payload": {
"id.$": "$.orderId",
"timestamp.$": "$$.State.EnteredTime"
}
},
"End": true
}
}
JSONata(Arguments を使った Lambda 呼び出しの例):
{
"CallLambda": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Arguments": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
"Payload": {
"id": "{% $states.input.orderId %}",
"timestamp": "{% $states.context.State.EnteredTime %}"
}
},
"End": true
}
}
このように、JSONPath(Parameters)と JSONata(Arguments)では、パラメータの指定形式が大きく異なります。
自身で実装する際や Step Functions のJSONをAIに生成させる際は、「JSONataの形式で書いて」などのようにプロンプトでの形式の指定をするとミスが防げます。
引っかかったポイント②:Payloadのサイズ上限を超過する
Step Functions の 実行時の入出力は 256 KiB が上限です。これは Lambda を含む、TaskやState、Execution の入出力全てに適用されます。
Lambda の呼び出しにおいてはリクエスト/レスポンスそれぞれ最大 6 MB まで許容されますが、Step Functions 経由でやり取りする場合は Step Functions の 256 KiB 制約が適用されます。
画像ファイルなど大きなデータは以下のように S3 にアップロードして、S3 のバケット/キーをペイロードの参照として渡し、 Lambda 側で S3 を読み取って処理する等の対処を検討する必要があります。
- 実例(S3参照でPayloadの容量問題を回避する)
{
"CallLambda": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Arguments": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:ProcessLargeObject",
"Payload": {
"s3Bucket": "[バケット名]",
"s3Key": "[キー名].json"
}
},
"End": true
}
}
引っかかったポイント③:Step Functions内で主要ロジックに絡む処理をしてしまう
実装の話というよりは設計の話になりますが、全体のアーキテクチャとして、主要なロジックは Lambda で扱い、Step Functions は Lambda の実行順を管理し、順番どおりに実行することに専念させる、という責務分担を想定していました。
しかし、実装に行き詰まった結果、本来は Lambda 側で扱うべき値の抽出や生成といった処理を Step Functions 側で行おうとしてしまい、その実装方針について指摘を受ける場面がありました。
たとえば、「注文完了メールを送信する」が主要ロジックの場合、以下のPayloadの抽出を Step Functionsでやるのは不適です。
NG イメージ(一部)
"BuildMailBody": {
"Type": "Pass",
"Parameters": {
"subject.$": "States.Format('【注文完了のお知らせ】{}様', $.user.name)",
"body.$": "States.Format('ご注文ありがとうございます。金額: {}円、注文ID: {}', States.MathMultiply($.order.price, $.order.count), $.order.id)"
},
"Next": "SendMail"
}
こうすると責務分けがあいまいとなり、今後改修を入れる場合に Lambda だけでなく Step Functions もいじる必要が出てくるかもしれません。また、Step Functions がメール送信ロジックの一部を担ってしまい、主要ロジックが正常に機能しているかのテストがしづらくなります(テスト範囲が広がってしまう)
OKイメージ:値は Lambdaに渡すだけで、主要ロジックは Lambda でやらせる
"SendOrderMail": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:sendOrderMail",
"Retry": [{
"ErrorEquals": ["States.ALL"],
"MaxAttempts": 3
}],
"End": true
}
<番外編>:リージョンの指定ミス
AWSを触っていたらどこでも発生するものですが、「この前作成したステートマシンなくなってる」と思って画面を見ていたら「リージョンがアメリカのバージニア北部(us-east-1)になってる...」ということが何回かありました。再ログインしたらリージョンが自身の作成した場所になっているか確認するようにしましょう。
まとめ
AWS の Black Belt や公式チュートリアルを通して、「実装自体は問題なくできる」と感じていました。
しかし、実際のユースケースに沿って実装しようとすると想像以上に複雑で、単に「実装できる/できない」だけでなく、性能面やコスト面も含めて考慮する必要があり、非常に大変でした。
この記事を通して、これから Step Functions を実装する開発者が同じ壁にぶつからないようにできたら幸いです。
<参考記事>
- Step Functions を使用して AWS Lambda 関数を呼び出す
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-lambda.html - 変数を使用したステート間のデータ受け渡し
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/workflow-variables.html - Step Functions での JSONata を使用したデータ変換
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/transforming-data.html