AWS Step Functions Local モック統合機能を利用したローカルテスト
先日、AWS Step Functions(以下 Step Functions)を使ったバッチジョブワークフローの実行基盤構築の記事を公開致しました(AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digital)。
Step Functionsは上記記事で記載した通り、アプリケーションの一連のワークフローを構築・管理するマネージドなサーバレスワークフローサービスで、様々なAWSサービスと連携したワークフローを容易に構築できます。
非常に便利なサービスですが、Step Functionsで構築したワークフローをテストしたい場合、連携先の各サービスが正常に稼働していなければいけません。開発の流れを考えると基盤レイヤであるワークフローは各サービスが作られる前に、事前に確認したい場面もあるかと思います。
2019年2月にローカル環境でStep Functionsをテスト出来る「Step Functions Local」が利用できるようになりました(AWS Step Functions ワークフローをローカルで開発してテストする)。
本記事では、Step Functions Localを使用して、上記記事で記載したワークフローのローカルテストを実施してみた結果を共有させていただきたいと思います。Step Functions Local
Step Functions Localはローカル環境でStep Functionsのワークフローをシミュレーション出来るツールです。
Javaで実装されており、JarかDockerコンテナ実行で使用可能です。
Jar利用
Step Functions Local (ダウンロード可能バージョン) のセットアップ-Java バージョン – AWS Step Functions
Dockerイメージ利用
Step Functions Local (ダウンロード可能なバージョン) と Docker のセットアップ – AWS Step Functions
Step Functions Local自体はStep Functionsをローカルで実行できるものであり、呼び出すサービスは実際のAWS環境で立ち上げるか、下記公式ページのようにローカルで必要なサービスを起動する必要があります。
Step Functions テストとAWS SAMCLI Local
しかしながら、前述の通りまだ連携先のサービスがまだ作られてない時点での事前テストを想定した場合、その為のモック的なサービスを自前で構築するのには手間がかかります。
このニーズを満たすものとして、先日Step Functions Localのモックサービス統合機能がリリースされました(Mocking service integrations with AWS Step Functions Local)。
※ここの冒頭で「Often, developers want to test the control and data flows of their state machine executions in isolation, without any dependency on service integration availability」と記載されている通り、まさに現場のニーズを鑑みた機能追加であると読み取れます。
モックテスト検証
では、先日の記事に記載したワークフローをモックテストしてみたいと思います。
構成としては、親のステートマシンでジョブワークフロー(いわゆるジョブネット)を管理し、実際の処理は呼び出し先の子ステートマシンで実行されます。

※上図のJOB1は同期、JOB2以降は非同期呼び出しとなります
※この構成を採用・検証した背景などは当該記事をご参照ください
(再掲 AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digital)
なお、執筆時点では複数ステートマシンを全てつなげたモックテストは実行出来ません。 あくまで個々のステートマシン単位でのテストとなりますのでご注意ください。
では、本構成の非同期呼び出しの子ステートマシン単体(job2に該当する下図赤枠のステートマシン)のモックテストを実施してみます。
1. ステートマシン定義
まず、テストしたいステートマシンのワークフロー定義ファイルを作成します。
※執筆時点では、以下の通りステートマシン定義はJSONのみ対応しています
create-state-machine – AWS CLI 2.7.2 Command Reference
{ "StartAt": "GetStatus", "States": { "GetStatus": { "Type": "Task", "Resource": "arn:aws:states:::dynamodb:getItem", "Parameters": { "TableName": "ddbt-coe-sfn-stg0-status-mng", "Key": { "id": { "S.$": "$.frame_id" }, "job_id": { "S.$": "$.job_id" } } }, "InputPath": "$", "ResultPath": "$.DynamoDB", "Next": "ChoiceState" }, "ChoiceState": { "Type": "Choice", "Choices": [ { "Variable": "$.DynamoDB.Item", "IsPresent": false, "Next": "GenerateRandomIntValue" } ], "Default": "TaskTokenHandler", "InputPath": "$" }, "GenerateRandomIntValue": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "Retry": [ { "ErrorEquals": [ "Lambda.ServiceException" ], "IntervalSeconds": 5, "MaxAttempts": 3, "BackoffRate": 1.0 } ], "Catch": [ { "ErrorEquals": [ "States.ALL" ], "ResultPath": "$.error", "Next": "TaskFailureHandler" } ], "InputPath": "$", "ResultPath": "$.intValue", "Next": "RegisterResult" }, "RegisterResult": { "Type": "Task", "Resource": "arn:aws:states:::dynamodb:putItem", "Parameters": { "TableName": "ddbt-coe-sfn-stg0-status-mng", "Item": { "id": { "S.$": "$.frame_id" }, "job_id.$": "$.job_id", "status": "0" } }, "InputPath": "$", "ResultPath": "$.DynamoDB", "Next": "TaskTokenHandler" }, "TaskTokenHandler": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler", "Parameters": { "taskToken.$": "$.TaskToken", "err": "false" }, "InputPath": "$", "ResultPath": "$.result", "Next": "PassToEnd" }, "TaskFailureHandler": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler", "Parameters": { "taskToken.$": "$.TaskToken", "err": "true" }, "InputPath": "$", "ResultPath": "$.result", "Next": "Failed" }, "PassToEnd": { "Type": "Pass", "End": true }, "Failed": { "Type": "Fail", "Error": "States.TaskFailed" } } }
上記ワークフローの流れは以下の通りです。
- GetStatusステート
- ChoiceStateステート
- GenerateRandomValueステート
- RegisterResultステート
- TaskTokenHandlerステート
- TaskFailureHandlerステート
- PassToEndステート
- Failedステート
親ステートマシンから渡されるFrameId(バッチの一連のシーケンス実行単位を表すID)とJobIdをキーにDynamoDBから実行済みのレコードを取得する。
取得後、②のステートへ遷移。
①の取得でレコードが存在していれば「実行済み」と判断し、⑤のステートへ遷移(③のステートをスキップ)。
レコードが存在していなければ「未実行」と判断し、③のステートへ遷移。
ランダム値を返すだけの単純なLambda関数をコールする。
(アプリケーションの処理の内容に意味はありません)
処理が成功した場合は④のステートへ、失敗した場合は⑥のステートへ遷移。
DynamoDBに実行済みレコードを登録する。
登録後⑤のステートへ遷移。
親ステートマシンへ正常終了のコールバックを送信するLambda関数をコールする。
コールバック後、⑦のステートへ遷移。
親ステートマシンへ異常終了のコールバックを送信するLambda関数をコールする。
コールバック後、当該フローを異常終了とさせる為、⑧のステートへ遷移。
End(正常終了)へパスするステート。
Type=Choiceのステート(②ChoiceState)から直接Endへ遷移出来ない仕様の為、正常終了時は共通的に本ステートを通す。
当該フローを異常終了とさせる。
2. モック設定ファイル
次にモック設定ファイルを作成します。
設定方法や各種フィールドのリファレンスは以下をご参照ください。
モックサービス統合の設定ファイル – AWS Step Functions
設定は2つのレイヤで構成されています。
StateMachines
モックサービス統合を使用するステートマシンの定義。
「TestCases」フィールドがそのステートマシンの個々のテストケースを表す。
MockedResponses
テストケースから呼ばれるモックレスポンスの設定。
今回は以下のように設定致しました。
MockConfigFile.json
{ "StateMachines": { "coe-job02-statemachine": { "TestCases": { "NormalPath": { "GetStatus": "MockedGetNoData", "GenerateRandomIntValue": "MockedMessages", "RegisterResult": "MockedResisterMessages", "TaskTokenHandler": "MockedHttpNormalResponse" }, "SkipPath": { "GetStatus": "MockedGetData", "TaskTokenHandler": "MockedHttpNormalResponse" }, "AnomalyPath": { "GetStatus": "MockedGetNoData", "GenerateRandomIntValue": "MockedMessagesErrorStatus", "TaskFailureHandler": "MockedHttpNormalResponse", "RegisterResult": "MockedResisterMessages", "TaskTokenHandler": "MockedHttpNormalResponse" } } } }, "MockedResponses": { "MockedGetData": { "0": { "Return": { "Item": "123456789" } } }, "MockedGetNoData": { "0": { "Return": {} } }, "MockedMessages": { "0": { "Return": { "intValue": "123" } } }, "MockedResisterMessages": { "0": { "Return": { "StatusCode": 200, "Payload": { "StatusCode": 200, "body": "OK" } } } }, "MockedMessagesErrorStatus": { "0-999": { "Throw": { "Error": "Lambda.ServiceException", "Cause": "exception occur." } } } } }
今回試したテストケースは以下3ケースとなります。
NormalPath
ジョブが未実行の正常系ケースで、初回実行時を想定。
SkipPath
既にジョブが実行済みの正常系ケースで、再実行時を想定。
AnomalyPath
「③GenerateRandomValueステート」が呼び出すLambda関数がエラーとなる異常系ケースで、リトライ試行回数分全てエラーとなる状況を想定。
3. Step Functions Localの起動
Step Functions Localを起動します。
前述した通り、Jar実行かDockerコンテナ実行かを選択できますが、今回はJar実行にて試してみます。
# モック設定ファイルの場所を環境変数に指定 export SFN_MOCK_CONFIG="[PATH_TO_MOCKFILE]/MockConfigFile.json" # リージョン指定 export AWS_DEFAULT_REGION=ap-northeast-1 # 起動 java -jar StepFunctionsLocal.jar
4. ステートマシンの登録
「1. ステートマシン定義」で定義したテスト対象のステートマシンを登録します。
# ステートマシンの登録 ※ロールはダミーでOK aws stepfunctions create-state-machine --endpoint http://localhost:8083 --definition file://statemachine/Job02-asl.json --name "coe-job02-statemachine" --role-arn "arn:aws:iam::123456789012:role/service-role/Dummy" # ログ 2022-03-31 18:24:57.777: [200] CreateStateMachine <= {"sdkResponseMetadata":null,"sdkHttpMetadata":null,"stateMachineArn":"arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine","creationDate":1648718697763}
5. ローカルテストの実行
準備が整いましたので、テスト実行します。
シナリオ1 (NormalPath)
NormalPathを実行します。
# NormalPath実行 aws stepfunctions start-execution --endpoint http://localhost:8083 --name coe-statemachine-`date +%Y%m%d-%H%M%S` --state-machine arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine#NormalPath --input "{\"frame_id\" : \"20220331-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}" { "executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220331-182725", "startDate": "2022-03-31T18:27:26.725000+09:00" }
実行時のログを確認します。
「1. ステートマシン定義」で記載したワークフローが「①→②→③→④→⑤→⑦」と実行されるのが想定結果です。
結果の確認は、実行しているStep Functions Localのコンソールログか、「get-execution-history」コマンドで確認できます。
本検証では「get-execution-history」コマンドで確認します。
get-execution-history – AWS CLI 1.25.1 Command Reference
各ステート(主にType=Task)の各イベントは正常系の場合、
「Entered→Scheduled→Started→Succeeded→Exited」となります。また、各イベントの「id及びpreviousEventId」を確認することで、各ステート、イベントの流れをトレースすることが出来ます。
以下の通り、NormalPathシナリオで、想定の動作をしていることが確認できました。
# get-execution-history aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220331-182725 { "events": [ { "timestamp": "2022-03-31T18:27:26.742000+09:00", "type": "ExecutionStarted", "id": 1, "previousEventId": 0, "executionStartedEventDetails": { "input": "{\"frame_id\" : \"20220331-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false }, "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy" } }, { "timestamp": "2022-03-31T18:27:26.743000+09:00", "type": "TaskStateEntered", "id": 2, "previousEventId": 0, "stateEnteredEventDetails": { "name": "GetStatus", "input": "{\"frame_id\" : \"20220331-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.759000+09:00", "type": "TaskScheduled", "id": 3, "previousEventId": 2, "taskScheduledEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "region": "ap-northeast-1", "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220331-001\"},\"job_id\":{\"S\":\"job2_1\"}}}" } }, { "timestamp": "2022-03-31T18:27:26.760000+09:00", "type": "TaskStarted", "id": 4, "previousEventId": 3, "taskStartedEventDetails": { "resourceType": "dynamodb", "resource": "getItem" } }, { "timestamp": "2022-03-31T18:27:26.777000+09:00", "type": "TaskSucceeded", "id": 5, "previousEventId": 4, "taskSucceededEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.779000+09:00", "type": "TaskStateExited", "id": 6, "previousEventId": 5, "stateExitedEventDetails": { "name": "GetStatus", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.782000+09:00", "type": "ChoiceStateEntered", "id": 7, "previousEventId": 6, "stateEnteredEventDetails": { "name": "ChoiceState", "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.785000+09:00", "type": "ChoiceStateExited", "id": 8, "previousEventId": 7, "stateExitedEventDetails": { "name": "ChoiceState", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.786000+09:00", "type": "TaskStateEntered", "id": 9, "previousEventId": 8, "stateEnteredEventDetails": { "name": "GenerateRandomIntValue", "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.788000+09:00", "type": "LambdaFunctionScheduled", "id": 10, "previousEventId": 9, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.788000+09:00", "type": "LambdaFunctionStarted", "id": 11, "previousEventId": 10 }, { "timestamp": "2022-03-31T18:27:26.791000+09:00", "type": "LambdaFunctionSucceeded", "id": 12, "previousEventId": 11, "lambdaFunctionSucceededEventDetails": { "output": "{\"intValue\":\"123\"}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.792000+09:00", "type": "TaskStateExited", "id": 13, "previousEventId": 12, "stateExitedEventDetails": { "name": "GenerateRandomIntValue", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"intValue\":\"123\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.793000+09:00", "type": "TaskStateEntered", "id": 14, "previousEventId": 13, "stateEnteredEventDetails": { "name": "RegisterResult", "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"intValue\":\"123\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.793000+09:00", "type": "TaskScheduled", "id": 15, "previousEventId": 14, "taskScheduledEventDetails": { "resourceType": "dynamodb", "resource": "putItem", "region": "ap-northeast-1", "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Item\":{\"id\":{\"S\":\"20220331-001\"},\"status\":\"0\",\"job_id\":\"job2_1\"}}" } }, { "timestamp": "2022-03-31T18:27:26.802000+09:00", "type": "TaskStarted", "id": 16, "previousEventId": 15, "taskStartedEventDetails": { "resourceType": "dynamodb", "resource": "putItem" } }, { "timestamp": "2022-03-31T18:27:26.803000+09:00", "type": "TaskSucceeded", "id": 17, "previousEventId": 16, "taskSucceededEventDetails": { "resourceType": "dynamodb", "resource": "putItem", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.803000+09:00", "type": "TaskStateExited", "id": 18, "previousEventId": 17, "stateExitedEventDetails": { "name": "RegisterResult", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.804000+09:00", "type": "TaskStateEntered", "id": 19, "previousEventId": 18, "stateEnteredEventDetails": { "name": "TaskTokenHandler", "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.805000+09:00", "type": "LambdaFunctionScheduled", "id": 20, "previousEventId": 19, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler", "input": "{\"err\":\"false\",\"taskToken\":\"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.806000+09:00", "type": "LambdaFunctionStarted", "id": 21, "previousEventId": 20 }, { "timestamp": "2022-03-31T18:27:26.806000+09:00", "type": "LambdaFunctionSucceeded", "id": 22, "previousEventId": 21, "lambdaFunctionSucceededEventDetails": { "output": "{\"StatusCode\":200,\"body\":\"OK\"}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.807000+09:00", "type": "TaskStateExited", "id": 23, "previousEventId": 22, "stateExitedEventDetails": { "name": "TaskTokenHandler", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.807000+09:00", "type": "PassStateEntered", "id": 24, "previousEventId": 23, "stateEnteredEventDetails": { "name": "PassToEnd", "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.808000+09:00", "type": "PassStateExited", "id": 25, "previousEventId": 24, "stateExitedEventDetails": { "name": "PassToEnd", "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-03-31T18:27:26.810000+09:00", "type": "ExecutionSucceeded", "id": 26, "previousEventId": 25, "executionSucceededEventDetails": { "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } } ] }
シナリオ2(SkipPath)
SkipPathを実行します。
# SkipPath実行 aws stepfunctions start-execution --endpoint http://localhost:8083 --name coe-statemachine-`date +%Y%m%d-%H%M%S` --state-machine arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine#SkipPath --input "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}" { "executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220401-124831", "startDate": "2022-04-01T12:48:32.874000+09:00" }
実行時のログを確認します。
「1. ステートマシン定義」で記載したワークフローが「①→②→⑤→⑦」と実行される(実行済みの為、③④はスキップ)のが想定結果です。
以下の通り、想定通りの結果となっていることが確認できました。
# get-execution-history aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220401-124831 { "events": [ { "timestamp": "2022-04-01T12:48:32.878000+09:00", "type": "ExecutionStarted", "id": 1, "previousEventId": 0, "executionStartedEventDetails": { "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false }, "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy" } }, { "timestamp": "2022-04-01T12:48:32.880000+09:00", "type": "TaskStateEntered", "id": 2, "previousEventId": 0, "stateEnteredEventDetails": { "name": "GetStatus", "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.890000+09:00", "type": "TaskScheduled", "id": 3, "previousEventId": 2, "taskScheduledEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "region": "ap-northeast-1", "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220401-001\"},\"job_id\":{\"S\":\"job2_1\"}}}" } }, { "timestamp": "2022-04-01T12:48:32.893000+09:00", "type": "TaskStarted", "id": 4, "previousEventId": 3, "taskStartedEventDetails": { "resourceType": "dynamodb", "resource": "getItem" } }, { "timestamp": "2022-04-01T12:48:32.898000+09:00", "type": "TaskSucceeded", "id": 5, "previousEventId": 4, "taskSucceededEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.899000+09:00", "type": "TaskStateExited", "id": 6, "previousEventId": 5, "stateExitedEventDetails": { "name": "GetStatus", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.899000+09:00", "type": "ChoiceStateEntered", "id": 7, "previousEventId": 6, "stateEnteredEventDetails": { "name": "ChoiceState", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.904000+09:00", "type": "ChoiceStateExited", "id": 8, "previousEventId": 7, "stateExitedEventDetails": { "name": "ChoiceState", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.904000+09:00", "type": "TaskStateEntered", "id": 9, "previousEventId": 8, "stateEnteredEventDetails": { "name": "TaskTokenHandler", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.906000+09:00", "type": "LambdaFunctionScheduled", "id": 10, "previousEventId": 9, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler", "input": "{\"err\":\"false\",\"taskToken\":\"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.906000+09:00", "type": "LambdaFunctionStarted", "id": 11, "previousEventId": 10 }, { "timestamp": "2022-04-01T12:48:32.907000+09:00", "type": "LambdaFunctionSucceeded", "id": 12, "previousEventId": 11, "lambdaFunctionSucceededEventDetails": { "output": "{\"StatusCode\":200,\"body\":\"OK\"}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.908000+09:00", "type": "TaskStateExited", "id": 13, "previousEventId": 12, "stateExitedEventDetails": { "name": "TaskTokenHandler", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.909000+09:00", "type": "PassStateEntered", "id": 14, "previousEventId": 13, "stateEnteredEventDetails": { "name": "PassToEnd", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.909000+09:00", "type": "PassStateExited", "id": 15, "previousEventId": 14, "stateExitedEventDetails": { "name": "PassToEnd", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T12:48:32.909000+09:00", "type": "ExecutionSucceeded", "id": 16, "previousEventId": 15, "executionSucceededEventDetails": { "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } } ] }
シナリオ3(AnomalyPath)
AnomalyPathを実行します。
# AnomalyPath実行 aws stepfunctions start-execution --endpoint http://localhost:8083 --name coe-statemachine-`date +%Y%m%d-%H%M%S` --state-machine arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine#AnomalyPath --input "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}" { "executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220401-145907", "startDate": "2022-04-01T14:59:08.005000+09:00" }
実行時のログを確認します。
「1. ステートマシン定義」で記載したワークフローが「①→②→③→⑥→⑧」と実行される(異常終了となる)のが想定結果です。
ポイントは、「1. ステートマシン定義」の「③GenerateRandomValueステート」がエラー時に最大「3回(5秒毎)」リトライしてるが、モックレスポンスは常にエラーを返却する為、「3回」リトライ後(計4回)に異常終了となる点です。
GenerateRandomValueステート定義抜粋
※リトライ定義
"Retry": [ { "ErrorEquals": [ "States.ALL" ], "IntervalSeconds": 5, "MaxAttempts": 3, "BackoffRate": 1.0 } ],
モック設定抜粋
※常にエラーを返す
"MockedMessagesErrorStatus": { "0-999": { "Throw": { "Error": "Lambda.ServiceException", "Cause": "exception occur." } } },
以下の通り、想定通りの結果となっていることが確認できました。
※GenerateRandomValueステートが4回試行された後、異常終了になっていることがわかります
→4度の試行失敗は「140、169、198、227行目」に出力されております
140行目: “timestamp”: “2022-04-01T14:59:08.054000+09:00”,
169行目: “timestamp”: “2022-04-01T14:59:13.059000+09:00”,
198行目: “timestamp”: “2022-04-01T14:59:18.075000+09:00”,
227行目: “timestamp”: “2022-04-01T14:59:23.084000+09:00”,
# get-execution-history aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn arn:aws:states:ap-northeast-1:123456789012:execution :coe-job02-statemachine:coe-statemachine-20220401-124831 { "events": [ { "timestamp": "2022-04-01T14:59:08.018000+09:00", "type": "ExecutionStarted", "id": 1, "previousEventId": 0, "executionStartedEventDetails": { "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false }, "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy" } }, { "timestamp": "2022-04-01T14:59:08.019000+09:00", "type": "TaskStateEntered", "id": 2, "previousEventId": 0, "stateEnteredEventDetails": { "name": "GetStatus", "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.031000+09:00", "type": "TaskScheduled", "id": 3, "previousEventId": 2, "taskScheduledEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "region": "ap-northeast-1", "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220401-001\"},\"job_id\":{\"S\":\"job2_1\"}}}" } }, { "timestamp": "2022-04-01T14:59:08.032000+09:00", "type": "TaskStarted", "id": 4, "previousEventId": 3, "taskStartedEventDetails": { "resourceType": "dynamodb", "resource": "getItem" } }, { "timestamp": "2022-04-01T14:59:08.043000+09:00", "type": "TaskSucceeded", "id": 5, "previousEventId": 4, "taskSucceededEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.044000+09:00", "type": "TaskStateExited", "id": 6, "previousEventId": 5, "stateExitedEventDetails": { "name": "GetStatus", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.046000+09:00", "type": "ChoiceStateEntered", "id": 7, "previousEventId": 6, "stateEnteredEventDetails": { "name": "ChoiceState", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.048000+09:00", "type": "ChoiceStateExited", "id": 8, "previousEventId": 7, "stateExitedEventDetails": { "name": "ChoiceState", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.048000+09:00", "type": "TaskStateEntered", "id": 9, "previousEventId": 8, "stateEnteredEventDetails": { "name": "GenerateRandomIntValue", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.050000+09:00", "type": "LambdaFunctionScheduled", "id": 10, "previousEventId": 9, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:08.051000+09:00", "type": "LambdaFunctionStarted", "id": 11, "previousEventId": 10 }, { "timestamp": "2022-04-01T14:59:08.054000+09:00", "type": "LambdaFunctionFailed", "id": 12, "previousEventId": 11, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "exception occur." } }, { "timestamp": "2022-04-01T14:59:13.057000+09:00", "type": "LambdaFunctionScheduled", "id": 13, "previousEventId": 12, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:13.058000+09:00", "type": "LambdaFunctionStarted", "id": 14, "previousEventId": 13 }, { "timestamp": "2022-04-01T14:59:13.059000+09:00", "type": "LambdaFunctionFailed", "id": 15, "previousEventId": 14, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "exception occur." } }, { "timestamp": "2022-04-01T14:59:18.074000+09:00", "type": "LambdaFunctionScheduled", "id": 16, "previousEventId": 15, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:18.075000+09:00", "type": "LambdaFunctionStarted", "id": 17, "previousEventId": 16 }, { "timestamp": "2022-04-01T14:59:18.075000+09:00", "type": "LambdaFunctionFailed", "id": 18, "previousEventId": 17, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "exception occur." } }, { "timestamp": "2022-04-01T14:59:23.082000+09:00", "type": "LambdaFunctionScheduled", "id": 19, "previousEventId": 18, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.082000+09:00", "type": "LambdaFunctionStarted", "id": 20, "previousEventId": 19 }, { "timestamp": "2022-04-01T14:59:23.084000+09:00", "type": "LambdaFunctionFailed", "id": 21, "previousEventId": 20, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "exception occur." } }, { "timestamp": "2022-04-01T14:59:23.086000+09:00", "type": "TaskStateExited", "id": 22, "previousEventId": 18, "stateExitedEventDetails": { "name": "GenerateRandomIntValue", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.087000+09:00", "type": "TaskStateEntered", "id": 23, "previousEventId": 22, "stateEnteredEventDetails": { "name": "TaskFailureHandler", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.088000+09:00", "type": "LambdaFunctionScheduled", "id": 24, "previousEventId": 23, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler", "input": "{\"err\":\"true\",\"taskToken\":\"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.088000+09:00", "type": "LambdaFunctionStarted", "id": 25, "previousEventId": 24 }, { "timestamp": "2022-04-01T14:59:23.090000+09:00", "type": "LambdaFunctionSucceeded", "id": 26, "previousEventId": 25, "lambdaFunctionSucceededEventDetails": { "output": "{\"StatusCode\":200,\"body\":\"OK\"}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.092000+09:00", "type": "TaskStateExited", "id": 27, "previousEventId": 26, "stateExitedEventDetails": { "name": "TaskFailureHandler", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.095000+09:00", "type": "FailStateEntered", "id": 28, "previousEventId": 27, "stateEnteredEventDetails": { "name": "Failed", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T14:59:23.097000+09:00", "type": "ExecutionFailed", "id": 29, "previousEventId": 28, "executionFailedEventDetails": { "error": "States.TaskFailed" } } ] }
なお、モック設定を以下にすることで、リトライ試行内で正常レスポンスを返却でき、フローとして正常終了することも確認可能です。
モック設定抜粋
※3回はエラー、4回目に正常を返す
"MockedMessagesErrorStatus": { "0-2": { "Throw": { "Error": "RetryUntilSentStatusException", "Cause": "not ready." } }, "3": { "Return": { "StatusCode": 200, "Payload": { "StatusCode": 200, "body": "OK" } } } },上記設定で実行すると、3回リトライ後正常終了することがわかります。
{ "events": [ { "timestamp": "2022-04-01T16:09:36.496000+09:00", "type": "ExecutionStarted", "id": 1, "previousEventId": 0, "executionStartedEventDetails": { "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false }, "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy" } }, { "timestamp": "2022-04-01T16:09:36.497000+09:00", "type": "TaskStateEntered", "id": 2, "previousEventId": 0, "stateEnteredEventDetails": { "name": "GetStatus", "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.507000+09:00", "type": "TaskScheduled", "id": 3, "previousEventId": 2, "taskScheduledEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "region": "ap-northeast-1", "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220401-001\"},\"job_id\":{\"S\":\"job2_1\"}}}" } }, { "timestamp": "2022-04-01T16:09:36.508000+09:00", "type": "TaskStarted", "id": 4, "previousEventId": 3, "taskStartedEventDetails": { "resourceType": "dynamodb", "resource": "getItem" } }, { "timestamp": "2022-04-01T16:09:36.521000+09:00", "type": "TaskSucceeded", "id": 5, "previousEventId": 4, "taskSucceededEventDetails": { "resourceType": "dynamodb", "resource": "getItem", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.522000+09:00", "type": "TaskStateExited", "id": 6, "previousEventId": 5, "stateExitedEventDetails": { "name": "GetStatus", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.523000+09:00", "type": "ChoiceStateEntered", "id": 7, "previousEventId": 6, "stateEnteredEventDetails": { "name": "ChoiceState", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.526000+09:00", "type": "ChoiceStateExited", "id": 8, "previousEventId": 7, "stateExitedEventDetails": { "name": "ChoiceState", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.526000+09:00", "type": "TaskStateEntered", "id": 9, "previousEventId": 8, "stateEnteredEventDetails": { "name": "GenerateRandomIntValue", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.528000+09:00", "type": "LambdaFunctionScheduled", "id": 10, "previousEventId": 9, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:36.528000+09:00", "type": "LambdaFunctionStarted", "id": 11, "previousEventId": 10 }, { "timestamp": "2022-04-01T16:09:36.532000+09:00", "type": "LambdaFunctionFailed", "id": 12, "previousEventId": 11, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "not ready." } }, { "timestamp": "2022-04-01T16:09:41.539000+09:00", "type": "LambdaFunctionScheduled", "id": 13, "previousEventId": 12, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:41.540000+09:00", "type": "LambdaFunctionStarted", "id": 14, "previousEventId": 13 }, { "timestamp": "2022-04-01T16:09:41.540000+09:00", "type": "LambdaFunctionFailed", "id": 15, "previousEventId": 14, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "not ready." } }, { "timestamp": "2022-04-01T16:09:46.554000+09:00", "type": "LambdaFunctionScheduled", "id": 16, "previousEventId": 15, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:46.554000+09:00", "type": "LambdaFunctionStarted", "id": 17, "previousEventId": 16 }, { "timestamp": "2022-04-01T16:09:46.555000+09:00", "type": "LambdaFunctionFailed", "id": 18, "previousEventId": 17, "lambdaFunctionFailedEventDetails": { "error": "Lambda.ServiceException", "cause": "not ready." } }, { "timestamp": "2022-04-01T16:09:51.558000+09:00", "type": "LambdaFunctionScheduled", "id": 19, "previousEventId": 18, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.559000+09:00", "type": "LambdaFunctionStarted", "id": 20, "previousEventId": 19 }, { "timestamp": "2022-04-01T16:09:51.562000+09:00", "type": "LambdaFunctionSucceeded", "id": 21, "previousEventId": 20, "lambdaFunctionSucceededEventDetails": { "output": "{\"StatusCode\":200,\"body\":\"OK\"}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.563000+09:00", "type": "TaskStateExited", "id": 22, "previousEventId": 21, "stateExitedEventDetails": { "name": "GenerateRandomIntValue", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.563000+09:00", "type": "TaskStateEntered", "id": 23, "previousEventId": 22, "stateEnteredEventDetails": { "name": "RegisterResult", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.564000+09:00", "type": "TaskScheduled", "id": 24, "previousEventId": 23, "taskScheduledEventDetails": { "resourceType": "dynamodb", "resource": "putItem", "region": "ap-northeast-1", "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Item\":{\"id\":{\"S\":\"20220401-001\"},\"status\":\"0\",\"job_id\":\"job2_1\"}}" } }, { "timestamp": "2022-04-01T16:09:51.565000+09:00", "type": "TaskStarted", "id": 25, "previousEventId": 24, "taskStartedEventDetails": { "resourceType": "dynamodb", "resource": "putItem" } }, { "timestamp": "2022-04-01T16:09:51.565000+09:00", "type": "TaskSucceeded", "id": 26, "previousEventId": 25, "taskSucceededEventDetails": { "resourceType": "dynamodb", "resource": "putItem", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.566000+09:00", "type": "TaskStateExited", "id": 27, "previousEventId": 26, "stateExitedEventDetails": { "name": "RegisterResult", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.566000+09:00", "type": "TaskStateEntered", "id": 28, "previousEventId": 27, "stateEnteredEventDetails": { "name": "TaskTokenHandler", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.567000+09:00", "type": "LambdaFunctionScheduled", "id": 29, "previousEventId": 28, "lambdaFunctionScheduledEventDetails": { "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler", "input": "{\"err\":\"false\",\"taskToken\":\"1234567890\"}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.568000+09:00", "type": "LambdaFunctionStarted", "id": 30, "previousEventId": 29 }, { "timestamp": "2022-04-01T16:09:51.568000+09:00", "type": "LambdaFunctionSucceeded", "id": 31, "previousEventId": 30, "lambdaFunctionSucceededEventDetails": { "output": "{\"StatusCode\":200,\"body\":\"OK\"}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.571000+09:00", "type": "TaskStateExited", "id": 32, "previousEventId": 31, "stateExitedEventDetails": { "name": "TaskTokenHandler", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.572000+09:00", "type": "PassStateEntered", "id": 33, "previousEventId": 32, "stateEnteredEventDetails": { "name": "PassToEnd", "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "inputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.573000+09:00", "type": "PassStateExited", "id": 34, "previousEventId": 33, "stateExitedEventDetails": { "name": "PassToEnd", "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } }, { "timestamp": "2022-04-01T16:09:51.575000+09:00", "type": "ExecutionSucceeded", "id": 35, "previousEventId": 34, "executionSucceededEventDetails": { "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}", "outputDetails": { "truncated": false } } } ] }
以上、実際のサービスを立ち上げることなく、ステートマシン単体でのワークフローのローカルテストが実行できました。
非同期連携部分のテスト
本構成においては、親ステートマシンから子ステートマシンをEventBridge経由で非同期に呼び出すような方式を採用しております。
※AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digitalより抜粋

本構成における親ステートマシンをStep Functions Localのモック統合でテスト可能かどうか確認しましたが、残念ながら執筆時点では非同期連携部分のテスト実施はできなそうです。
非同期となる為、親ステートマシンのフロー制御は、コールバックがどのタイミングで実行されるかに依存します。モック機能にコールバックのタイミングを制御するような機能はない為、現時点では実現は難しいと思っております。
よって、テストしたい場合は実際のAWS環境もしくはローカルにてEventBridge等の必要なサービスを稼働させ、Step Functions Localからコールするような対応が必要になるかと思います。
※Step Functions Localがコールするエンドポイント等は以下で設定可能です
Step Functions コンフィギュレーションオプションのローカル設定 – AWS Step Functions
GitHub – localstack/localstack: A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline!
さいごに
Step Functions Localを使ったローカルテストを試してみました。
AWS環境へサービスをデプロイする前に、ワークフローのみ事前に稼働確認できることは非常にメリットがあると感じました。事前確認することで、品質的に担保された状態で実際のサービスと連携できるようになりますし、うまく動作しなかった場合の切り分けも容易になると思います。
興味ある方は是非お試しいただければと思います。
以上