はじめに
こんばんは、mirukyです。
Step Functionsの途中で連携先が止まり、原因を直したあとに最初からやり直した経験はありませんか。前半の処理がすでに成功しているなら、その結果を残したまま続きへ戻りたいところですよね〜。
今回はAWSマネジメントコンソールだけを使い、失敗したStandardワークフローをRedriveします。成功済みのPrepareが1回のまま、失敗したRunTaskから続いたことをイベント履歴でサクッと確かめます。
目次
1. Redriveの挙動
Redriveは、正常に完了できなかったStandardワークフローを失敗した状態から再開する機能です。元と同じ入力、ステートマシン定義、実行ARNを使い、成功済み状態の結果と履歴を引き継ぎます。
新しく開始する場合やRetryとは、動くタイミングと範囲が異なります。
| 方法 | 動くタイミング | 再実行する範囲 |
|---|---|---|
| 新しい実行 | 任意の時点 | 最初の状態から |
Retry |
実行中 | ASLへ書いた条件に従って自動再試行 |
| Redrive | 実行が失敗、中止、タイムアウトした後 | 完了できなかった状態から |
対象はStandardワークフローです。終了から14日以内、履歴が24,999イベント未満などが利用条件です。元の定義は変更できないため、定義を直す場合は最初から開始し直してください。IAM権限やLambdaの環境変数、接続先の停止といった定義外の原因を直せば、元の履歴を引き継いでRedriveできます。
2. 今回作るワークフロー
東京リージョンap-northeast-1へ、一時的なLambda関数とStandardワークフローを作ります。
| 状態 | 種類 | 役割 |
|---|---|---|
Prepare |
Pass | 入力へprepared: trueを加える |
RunTask |
Task | Lambda関数を呼び出す |
Done |
Succeed | ワークフローを正常終了する |
Lambdaの環境変数SHOULD_FAILを最初はtrueにし、RunTaskで例外を発生させます。値をfalseへ変えたあと、失敗した実行をRedriveする流れです。
Standardワークフローは状態遷移数に応じて課金され、LambdaとCloudWatch Logsにも料金が発生する場合があります。作業後は8章の順番で削除してください。
3. 失敗するLambda関数を作る
3-1. 関数を作成する
最初に、Step Functionsから呼び出すLambda関数を東京リージョンへ作ります。
撮影時の関数名はqiita-sfn-redrive-function-20260811-b7e29c4f、ランタイムはNode.js 24.xです。Lambda用の実行ロールは、この作成処理で新規発行する設定を使いました。
作成後は、コンソール内のコードエディタへ次のコードを貼り付け、変更を反映してください。
export const handler = async (event) => {
// Redrive前後で同じ入力を確認する
console.log(JSON.stringify({ event, shouldFail: process.env.SHOULD_FAIL }));
if (process.env.SHOULD_FAIL === "true") {
throw new Error("Intentional failure for the Redrive demo");
}
return {
status: "completed",
requestId: event.requestId,
};
};
SHOULD_FAILが文字列のtrueなら例外を投げ、正常時は入力のrequestIdを返すコードです。
3-2. 失敗条件を入れる
1回目の呼び出しを失敗させるため、Lambdaの環境変数へキーと値を追加してください。
環境変数はキーSHOULD_FAIL、値trueで保存されています。この値が、1回目の失敗原因です。
4. Standardワークフローを作る
Step Functionsで新規作成を始め、コード入力へ切り替えて次の定義を貼り付けます。<Lambda関数のARN>は3章で作った関数のARNへ置き換えてください。
{
"Comment": "Step Functions Redrive hands-on",
"StartAt": "Prepare",
"States": {
"Prepare": {
"Type": "Pass",
"Parameters": {
"requestId.$": "$.requestId",
"prepared": true
},
"Next": "RunTask"
},
"RunTask": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "<Lambda関数のARN>:$LATEST",
"Payload.$": "$"
},
"OutputPath": "$.Payload",
"TimeoutSeconds": 10,
"Next": "Done"
},
"Done": {
"Type": "Succeed"
}
}
}
定義の貼り付け後、作成内容を決めます。
名前はqiita-sfn-redrive-console-20260811-b7e29c4f、タイプはStandardです。Step Functions用の実行ロールも、この作成処理で新規発行する設定を使いました。
作成を終えたら、定義から生成されたワークフローを確認します。
作成後のグラフはPrepare、RunTask、Doneの順につながっています。RunTaskはLambda関数を呼び出すTask状態です。
5. 1回目の実行を失敗させる
作成したステートマシンを、次の入力で実行します。
{
"requestId": "demo-001"
}
SHOULD_FAIL=trueのままなので、Lambda関数は意図的な例外を返します。
実行名はredrive-demo-20260811-b7e29c4f、ステータスは失敗です。グラフではPrepareが成功し、RunTaskが失敗、Doneは未実行のままです。上部にはエラーになったステップとしてRunTaskが示され、障害からのリドライブも使える状態になりました。
6. Redriveで再開する
6-1. 失敗原因を取り除く
ステートマシン定義は変更せず、Lambdaの環境変数だけを変更します。
変更後の値はSHOULD_FAIL=falseです。これで同じ入力を受け取っても、Lambda関数は正常な応答を返します。
6-2. 同じ実行を再開する
失敗したワークフローの詳細へ戻り、Redriveを始めます。別の開始処理は行いません。
実行名は失敗時と同じredrive-demo-20260811-b7e29c4fのまま、ステータスが成功へ変わりました。リドライブ回数は1です。
7. 実行履歴を確認する
Redriveが本当に失敗地点から続いたのか、同じ実行のイベント履歴で確かめます。
履歴ではPrepareの開始と終了がID 2、3にあり、失敗後のExecutionRedrivenがID 9です。その直後はRunTaskのスケジュール、開始、成功がID 10、11、12へ続きます。ExecutionRedrivenと2回目のRunTaskの間にPrepareはなく、成功済み状態を再実行しない挙動を確認できました。
Redriveでは成功済みステップを動かさずに済みますが、失敗したTaskは先頭からもう一度動きます。Task内で外部サービスを更新する場合は、requestIdを保存して同じ要求を二重処理しない設計が必要です。
おわりに
ここまでお付き合いいただきありがとうございます。
Redrive後も実行名と履歴は同じまま残り、成功済みのPrepareを飛ばしてRunTaskから再開しました。復旧作業へ取り入れるときは、14日間などの利用条件と、再実行されるタスクの冪等性をあわせて確認してください。
ではまた、お会いしましょう。







