重试失败步骤并处理永久错误

AWSBeginner
立即练习

介绍

临时的依赖故障可能恢复,而无效订单应保持失败状态。你将配置选择性重试和永久失败路径,然后观察实际尝试和存储结果。

请先完成 构建多步骤订单工作流。这个全新 VM 提供自己的工作程序和空表;不会复用之前的状态机、角色或执行。

相关认证考点

本实验为以下认证考点提供动手练习。

授权工作流调用其工作程序

本步骤中,检查提供的业务工作程序,并为 Step Functions 创建独立执行角色。

使用 Terminal 旁的 AWS View,将 CLI 查询与本实验的实际资源和结果对比。保留提供的参考数据。

这个全新 VM 提供工作程序函数和独立的订单、诊断及参考表。尚无状态机或工作流角色。工作程序接受订单,写入数量和总金额,稍后还能读取摘要。对于模拟故障测试,flaky 模式会在第一次尝试中、任何业务写入之前抛出 TransientOrderError;permanent 模式会在写入之前抛出 InvalidOrder。诊断尝试与业务订单分开保存。其 Lambda 执行角色已授权这些作为配套环境的表操作。

从项目目录开始。shell 赋值保存返回的标识符;--query 选择响应字段,--output text 生成可复用字符串。

cd /home/labex/project
WORKER_NAME=labex-ev04-worker
WORKER_ARN=$(aws lambda get-function-configuration \
  --function-name labex-ev04-worker \
  --query FunctionArn \
  --output text)
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 服务代入该角色;权限策略允许所得会话只调用这个工作程序。带引号的 here-document 写入字面 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=$(aws iam create-role \
  --role-name labex-ev04-workflow-role \
  --assume-role-policy-document file://workflow-trust.json \
  --query Role.Arn \
  --output text)

写入普通权限文档。shell 插入 $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

策略只有一个精确的函数 ARN。Step Functions 不会获得工作程序的 DynamoDB 权限:工作程序使用独立的 Lambda 角色执行这些调用。运行授权检查。

配置有限重试与永久失败路径

本步骤中,构建具有选择性恢复行为的实际双任务工作流。

临时错误重试与永久失败

该工作程序的临时错误可以通过重试恢复。永久错误则通过 Catch 进入明确的失败结果。

ASL 的 Retry 列出可以重试的任务错误。ErrorEquals 必须匹配函数错误类型。IntervalSeconds:1 从一秒延迟开始;BackoffRate:2 将下一次延迟乘以二。MaxAttempts:2 允许初始尝试之后最多重试两次。此重试只处理 TransientOrderError,而不是所有可能的失败。

Catch 为任务未能恢复的错误选择另一个状态。ResultPath 将错误记录在 failure 下;Next 到达明确的 Fail 状态。处理错误不代表业务任务成功。永久的 InvalidOrder 会以 OrderRejected 结束为 FAILED,而不会假装订单已完成。

带引号的 here-document 写入字面 JSON。已有 Choice 拒绝非正数数量;两个 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
jq --arg worker "$WORKER_NAME" '.States.StoreOrder.Parameters.FunctionName=$worker | .States.ReadSummary.Parameters.FunctionName=$worker' workflow-template.json > workflow.json
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)
aws stepfunctions describe-state-machine \
  --state-machine-arn "$MACHINE_ARN" \
  --query '{Name:name,Definition:definition}'

定义包含选择性 retry 和 catch 配置块。AWS View 显示相同配置;尚无执行或业务订单。运行恢复定义检查。

观察实际重试、Catch 与权限结果

本步骤中,运行临时失败、永久失败和被拒绝的调用。

工作程序的第一次 flaky 尝试更新诊断尝试计数,并在写入订单之前抛出错误。下一次尝试可以成功。有次数上限的 shell 循环每两秒读取执行状态,直到不再运行;$(...) 捕获输出,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)
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,随后是两个 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)
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

一次实际工作程序调用以 InvalidOrder 失败;Catch 到达 Rejected,执行以 OrderRejected 结束为 FAILED。临时错误重试不会匹配此永久错误。没有 permanent-order 业务项目。

最后,只移除工作流的调用授权。操作员可以启动执行,但工作流角色无法调用工作程序:

aws iam delete-role-policy --role-name labex-ev04-workflow-role --policy-name InvokeWorker
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)
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 在唯一订单旁显示成功的重试摘要和两次失败。

下面的示例展示实际重试摘要、永久失败和被拒绝的执行,以及保存的订单。

AWS View 显示实际重试成功与永久失败

运行实际恢复检查。

移除工作流资源与模拟结果

本步骤中,删除你已完成执行的状态机、工作流角色、订单和日志,同时保留提供的配套资源。

三次执行都已结束。删除状态机会将它从活动状态机列表中移除。删除角色之前,先移除自己创建的角色策略,然后移除执行所创建的模拟订单和工作程序日志组。

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
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 进入明确的永久失败状态。原生历史与实际订单区分了尝试次数和业务写入;权限拒绝没有产生工作程序或业务影响。你移除了自己创建的工作流资源和结果,同时保留提供的配套资源。

下一个实验将处理业务写入之后发生的失败,此时重试可能重复产生业务效果。