失敗したステップを再試行して永続的なエラーを処理する

AWSBeginner
オンラインで実践に進む

はじめに

依存先の一時的な失敗は復旧する場合がありますが、無効な注文は失敗したままにすべきです。選択的な再試行と永続的な失敗の経路を設定し、実際の試行と保存された結果を観察します。

Step Functions は Lambda のタスクを調整します。用意されたワーカーは DynamoDB に注文を保存し、そのサマリーを読み取ります。CLI アクセスは設定済みで、個人の AWS アカウントやアクセスキーは不要です。

先に 複数ステップの注文ワークフローを構築する を完了してください。この新しい VM は、独自のワーカーと空のテーブルを提供します。以前のステートマシン、ロール、実行は再利用しません。

認定試験との関連

認定資格 試験タスク 実践内容
Solutions Architect – Associate (SAA-C03) タスク 2.1 ワークフローのエラー処理、回数を限定したリトライ、失敗結果。
Developer – Associate (DVA-C02) タスク 1.1 ワークフローのエラー処理、回数を限定したリトライ、失敗結果。
DevOps Engineer – Professional (DOP-C02) タスク 5.1 基礎演習:ワークフローのエラー処理、回数を限定したリトライ、失敗結果。
Solutions Architect – Professional (SAP-C02) タスク 2.4 基礎演習:ワークフローのエラー処理、回数を限定したリトライ、失敗結果。

ラボの概要

概念図:Step Functions が DynamoDB を読み書きする Lambda ワーカーを呼び出します。一時的なエラーは回数を制限したリトライで回復し、恒久的なエラーは Catch を通じて明示的な失敗になります。

ワークフローによるワーカー呼び出しを認可する

このステップでは、提供された業務ワーカーを確認し、Step Functions 用の独立した実行ロールを作成します。

上部の AWS View を使い、CLI クエリをこのラボの実際のリソースと結果と比較します。提供された参照データを保持してください。

この新しい VM は、ワーカー関数と独立した注文・診断・参照テーブルを提供します。ステートマシンやワークフローロールはまだありません。ワーカーは注文を受け取り、数量と合計を書き込み、後でサマリーを読み取れます。合成障害のテストでは、flaky モードが最初の試行で業務書き込み前に TransientOrderError を発生させ、permanent モードが書き込み前に InvalidOrder を発生させます。診断用の試行記録は業務注文とは別です。Lambda 実行ロールは、ワークフロー自体の操作とは別の、これらのテーブル操作をすでに認可しています。

プロジェクトディレクトリから始めます。シェルの代入は返された識別子を保存します。--query はレスポンスフィールドを選択し、--output text は再利用可能な文字列を生成します。

cd /home/labex/project
WORKER_NAME=labex-ev04-worker

返された値を WORKER_ARN に保存します:

WORKER_ARN=$(aws lambda get-function-configuration \
  --function-name labex-ev04-worker \
  --query FunctionArn \
  --output text)
echo "$WORKER_ARN"

関数のロールと環境設定を確認します:

aws lambda get-function-configuration \
  --function-name labex-ev04-worker \
  --query '{Name:FunctionName,Role:Role,Runtime:Runtime,Timeout:Timeout}'

残っているステートマシンを確認します:

aws stepfunctions list-state-machines

ワーカーは Python3.12 と独自の Lambda ロールを使い、ステートマシン一覧は空です。Step Functions には自身の実行ロールが必要です。信頼ポリシーは Step Functions サービスによるそのロールの引き受けを許可し、権限ポリシーは、生成されたセッションがこのワーカーだけを呼び出すことを許可します。引用符付きのヒアドキュメントは JSON をそのまま書き込み、file:// はそれをリクエストに読み込みます。

cat > workflow-trust.json <<'JSON'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "states.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
JSON

返された値を ROLE_ARN に保存します:

ROLE_ARN=$(aws iam create-role \
  --role-name labex-ev04-workflow-role \
  --assume-role-policy-document file://workflow-trust.json \
  --query Role.Arn \
  --output text)
echo "$ROLE_ARN"

通常の権限ドキュメントを書き込みます。シェルは $WORKER_ARN を挿入し、この権限付与を提供されたワーカーに限定します。

cat > workflow-invoke.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "lambda:InvokeFunction",
      "Resource": "$WORKER_ARN"
    }
  ]
}
EOF

アプリケーションロールのアクセス許可ポリシーを適用します:

aws iam put-role-policy \
  --role-name labex-ev04-workflow-role \
  --policy-name InvokeWorker \
  --policy-document file://workflow-invoke.json

アプリケーションロールのアクセス許可ポリシーを確認します:

aws iam get-role-policy --role-name labex-ev04-workflow-role --policy-name InvokeWorker

ポリシーには 1 つの正確な関数 ARN があります。Step Functions はワーカーの DynamoDB 権限を受け取りません。ワーカーは独立した Lambda ロールでそれらの呼び出しを行います。認可の検証を実行してください。

回数を限定した再試行と永続的な失敗の経路を設定する

このステップでは、選択的な復旧動作を持つ、実際の 2 タスクのワークフローを構築します。

一時的な失敗の再試行と永続的な失敗 — このワーカーの一時的なエラーは再試行で復旧できます。永続的なエラーは Catch を通り、明示的な失敗結果になります。

ASL の Retry は、再試行できるタスクエラーを列挙します。ErrorEquals は関数のエラー型に一致する必要があります。IntervalSeconds:1 は 1 秒の遅延から始め、BackoffRate:2 は次の遅延を倍にします。MaxAttempts:2 は、最初の試行後に最大 2 回の再試行を許可します。この再試行は、あらゆる失敗ではなく、TransientOrderError だけを扱います。

Catch は、タスクが復旧できなかったエラーに対して、別の状態を選択します。ResultPath はエラーを failure の下に記録し、Next は明示的な Fail 状態へ進みます。エラーを処理したからといって、業務ジョブが成功したとは限りません。永続的な InvalidOrder は、注文を完了したふりをせず、OrderRejected を伴う FAILED で終了します。

引用符付きのヒアドキュメントは JSON をそのまま書き込みます。既存の Choice は正ではない数量を拒否します。2 つの Task は、保存とその後のサマリー読み取りを行います。Payload.$ は現在の入力を渡し、ResultSelector は実際の関数ペイロードを保持し、ResultPath はそれを saved の下に保存し、OutputPath は実際のサマリーを返します。

cat > workflow-template.json <<'JSON'
{
  "StartAt": "CheckQuantity",
  "States": {
    "CheckQuantity": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.quantity",
          "NumericGreaterThan": 0,
          "Next": "StoreOrder"
        }
      ],
      "Default": "Rejected"
    },
    "StoreOrder": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "WORKER_NAME",
        "Payload.$": "$"
      },
      "ResultSelector": {
        "result.$": "$.Payload"
      },
      "ResultPath": "$.saved",
      "Retry": [
        {
          "ErrorEquals": [
            "TransientOrderError"
          ],
          "IntervalSeconds": 1,
          "BackoffRate": 2,
          "MaxAttempts": 2
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "InvalidOrder"
          ],
          "Next": "Rejected",
          "ResultPath": "$.failure"
        }
      ],
      "Next": "ReadSummary"
    },
    "ReadSummary": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "WORKER_NAME",
        "Payload": {
          "stage": "summary",
          "id.$": "$.saved.result.id"
        }
      },
      "OutputPath": "$.Payload",
      "End": true
    },
    "Rejected": {
      "Type": "Fail",
      "Error": "OrderRejected",
      "Cause": "Order could not be completed"
    }
  }
}
JSON

設定を workflow.json に書き込みます:

jq --arg worker "$WORKER_NAME" '.States.StoreOrder.Parameters.FunctionName=$worker | .States.ReadSummary.Parameters.FunctionName=$worker' workflow-template.json > workflow.json

返された値を MACHINE_ARN に保存します:

MACHINE_ARN=$(aws stepfunctions create-state-machine \
  --name labex-ev04-orders \
  --type STANDARD \
  --role-arn "$ROLE_ARN" \
  --definition file://workflow.json \
  --query stateMachineArn \
  --output text)
echo "$MACHINE_ARN"

ステートマシンの定義とロールを確認します:

aws stepfunctions describe-state-machine \
  --state-machine-arn "$MACHINE_ARN" \
  --query '{Name:name,Definition:definition}'

定義には、選択的な retry と catch のブロックが含まれます。AWS View にも同じ設定が表示され、実行や業務注文はまだありません。復旧定義の検証を実行してください。

実際の Retry、Catch、権限の結果を観察する

このステップでは、一時的な失敗、永続的な失敗、拒否される呼び出しを実行します。

ワーカーの最初の flaky 試行は、診断用試行カウンターを更新し、注文を書き込む前にエラーを発生させます。次の試行は成功できます。回数を限定したシェルループは、実行中でなくなるまで 2 秒ごとに状態を読み取ります。$(...) は出力を取得し、break はループを終了します。ループ後も実行が RUNNING の場合は、先へ進む前に確認してください。

エラー型に応じて対処を選びます。一時的なワーカーエラーは再試行の対象になり得ますが、恒久的な注文エラーには明示的な失敗結果が必要で、呼び出し拒否には範囲を限定した権限の復元が必要です。履歴と注文・試行テーブルから、ワーカーが実行され、データを書いたかを確認できます。

RETRY_ARN=$(aws stepfunctions start-execution \
  --state-machine-arn "$MACHINE_ARN" \
  --name retry-order \
  --input '{"id":"retry-order","quantity":2,"mode":"flaky"}' \
  --query executionArn \
  --output text)
echo "$RETRY_ARN"

この実行が RUNNING 状態を終了するまでポーリングします:

for attempt in $(seq 1 60); do
  STATUS=$(aws stepfunctions describe-execution \
    --execution-arn "$RETRY_ARN" \
    --query status \
    --output text)
  if test "$STATUS" != RUNNING; then break; fi
  sleep 2
done

実行の状態、出力、エラーの詳細を確認します:

aws stepfunctions describe-execution \
  --execution-arn "$RETRY_ARN" \
  --query '{Status:status,Output:output}'

実際のワークフロー実行履歴を確認します:

aws stepfunctions get-execution-history \
  --execution-arn "$RETRY_ARN" \
  --query 'events[?type==`TaskFailed` || type==`TaskSucceeded`].{Type:type,Error:taskFailedEventDetails.error}'

実際に保存されたデータベース項目を確認します:

aws dynamodb get-item \
  --table-name labex-ev04-orders \
  --key '{"id":{"S":"retry-order"}}' \
  --query Item

実際に保存されたデータベース項目を確認します:

aws dynamodb get-item \
  --table-name labex-ev04-attempts \
  --key '{"id":{"S":"retry-order"}}' \
  --query Item

最終状態は SUCCEEDED で、完了したサマリーは 2/600 です。履歴には、TransientOrderError の TaskFailed が 1 件あり、その後に保存成功とサマリー読み取りの 2 件の TaskSucceeded イベントがあります。ネイティブな注文は数量 2、合計 600 で、診断用試行回数は 2 です。再試行と業務書き込みの回数は異なります。

次に、ワーカーの永続的な失敗モードを使います。

PERMANENT_ARN=$(aws stepfunctions start-execution \
  --state-machine-arn "$MACHINE_ARN" \
  --name permanent-order \
  --input '{"id":"permanent-order","quantity":2,"mode":"permanent"}' \
  --query executionArn \
  --output text)
echo "$PERMANENT_ARN"

この実行が RUNNING 状態を終了するまでポーリングします:

for attempt in $(seq 1 30); do
  STATUS=$(aws stepfunctions describe-execution \
    --execution-arn "$PERMANENT_ARN" \
    --query status \
    --output text)
  if test "$STATUS" != RUNNING; then break; fi
  sleep 2
done

実行の状態、出力、エラーの詳細を確認します:

aws stepfunctions describe-execution \
  --execution-arn "$PERMANENT_ARN" \
  --query '{Status:status,Error:error}'

実際のワークフロー実行履歴を確認します:

aws stepfunctions get-execution-history \
  --execution-arn "$PERMANENT_ARN" \
  --query 'events[?type==`TaskFailed` || type==`FailStateEntered`].{Type:type,Error:taskFailedEventDetails.error,State:stateEnteredEventDetails.name}'

実際に保存されたデータベース項目を確認します:

aws dynamodb get-item \
  --table-name labex-ev04-orders \
  --key '{"id":{"S":"permanent-order"}}' \
  --query Item

1 回の実際のワーカー呼び出しが InvalidOrder で失敗します。Catch は Rejected に進み、実行は OrderRejected を伴う FAILED で終了します。一時的な失敗の再試行は、この永続的なエラーに一致しません。permanent-order の業務アイテムはありません。

最後に、ワークフローの呼び出し権限付与だけを削除します。操作担当者は実行を開始できますが、ワークフローロールはワーカーを呼び出せません。

aws iam delete-role-policy --role-name labex-ev04-workflow-role --policy-name InvokeWorker

返された値を DENIED_ARN に保存します:

DENIED_ARN=$(aws stepfunctions start-execution \
  --state-machine-arn "$MACHINE_ARN" \
  --name denied-order \
  --input '{"id":"denied-order","quantity":2,"mode":"flaky"}' \
  --query executionArn \
  --output text)
echo "$DENIED_ARN"

この実行が RUNNING 状態を終了するまでポーリングします:

for attempt in $(seq 1 30); do
  STATUS=$(aws stepfunctions describe-execution \
    --execution-arn "$DENIED_ARN" \
    --query status \
    --output text)
  if test "$STATUS" != RUNNING; then break; fi
  sleep 2
done

実行の状態、出力、エラーの詳細を確認します:

aws stepfunctions describe-execution \
  --execution-arn "$DENIED_ARN" \
  --query '{Status:status,Error:error}'

実際に保存されたデータベース項目を確認します:

aws dynamodb get-item \
  --table-name labex-ev04-orders \
  --key '{"id":{"S":"denied-order"}}' \
  --query Item

アプリケーションロールのアクセス許可ポリシーを適用します:

aws iam put-role-policy \
  --role-name labex-ev04-workflow-role \
  --policy-name InvokeWorker \
  --policy-document file://workflow-invoke.json

このアクセス拒否の実行は、ワーカーを呼び出さず、診断データも業務データも作成せずに失敗します。指定したアプリケーションエラーの再試行では、認可の不足を修復できません。意図する権限付与は復元されます。AWS View には、成功した再試行のサマリーと 2 つの失敗が、1 件の注文の隣に表示されます。

AWS View に実際の再試行成功と永続的な失敗が表示される — 以下の例は、実際の再試行のサマリー、永続的な失敗、拒否された実行を、保存された注文とともに示します。

実際の復旧の検証を実行してください。

ワークフローリソースと合成結果を削除する

このステップでは、提供された準備リソースを保持しながら、自分の完了したステートマシン、ワークフローロール、注文、ログを削除します。

3 つの実行はすべて終了しています。ステートマシンの削除は、アクティブなステートマシン一覧からそれを取り除きます。ロールを削除する前に所有するロールポリシーを削除し、その後、合成注文と実行によって作成されたワーカーロググループを削除します。

aws stepfunctions delete-state-machine --state-machine-arn "$MACHINE_ARN"

アプリケーションロールのアクセス許可ポリシーのみを削除します:

aws iam delete-role-policy --role-name labex-ev04-workflow-role --policy-name InvokeWorker

演習用ワークフロー実行ロールのみを削除します:

aws iam delete-role --role-name labex-ev04-workflow-role

実際に保存されたデータベース項目のみを削除します:

aws dynamodb delete-item --table-name labex-ev04-orders --key '{"id":{"S":"retry-order"}}'

実際に保存されたデータベース項目のみを削除します:

aws dynamodb delete-item \
  --table-name labex-ev04-attempts \
  --key '{"id":{"S":"retry-order"}}'

指定したアプリケーションロググループのみを削除します:

aws logs delete-log-group --log-group-name /aws/lambda/labex-ev04-worker

成功する一覧取得で、残るものを証明します。

aws stepfunctions list-state-machines

残っている IAM ロールを確認します:

aws iam list-roles --query 'Roles[].RoleName'

実際に保存された注文を確認します:

aws dynamodb scan --table-name labex-ev04-orders --query Items

残っているロググループを確認します:

aws logs describe-log-groups --query logGroups

演習と無関係な参照レコードを確認します:

aws dynamodb scan --table-name labex-ev04-reference --query Items

アクティブなステートマシン、注文、ロググループはありません。提供されたワーカーロールだけが残り、参照アイテムは変わりません。提供されたワーカーとテーブルは保持してください。準備時の所有範囲は、自分が作成したワークフローやリソースと異なります。ネットワークエラーや認証エラーは削除の証拠になりません。

このラボで作成した通常のファイルを削除します。

rm -f workflow-trust.json workflow-invoke.json workflow-template.json workflow.json

AWS View には、空のステートマシン・実行・注文と、保持された参照リソースが表示されます。VM を終了する前に、後片付けの検証を実行してください。

まとめ

特定の一時的な失敗に対して回数を限定した再試行を設定し、Catch で明示的な永続的失敗へ進むようにしました。ネイティブな履歴と実際の注文で、試行と業務書き込みを区別しました。権限拒否では、ワーカー実行も業務上の効果も発生しませんでした。提供された準備リソースを保持しながら、所有するワークフローリソースと結果を削除しました。

次のユニットでは、再試行すると効果が繰り返される可能性のある、業務書き込み後の失敗を扱います。

詳しくは、AWS 公式ドキュメントを参照してください:Retry、バックオフ、Catch によるエラー処理。