Step Functions 内で発生したエラーのエラーハンドリングを実装する

AWS

はじめに

Step Functions 内の処理でエラーが発生したときに、特定の処理を行わせたいことがあると思います。
その際の取りうるパターンを整理します。

対象者

この記事は下記のような人を対象にしています。

  • Step Functions 内の処理でエラーが発生した時に、特定の処理を行わせたい人
  • ステートマシンでタスクフローを作成したい人

StepFunctions 内で完結させるパターン

タスクごとに Catch する

各タスクごとに Catch フィールドを使用することで、エラーをキャッチすることができます。
以下の図からも分かるように、冗長になっていて複雑なステートマシンになると、どういうフローかが視覚的に分かりにくくなります。

ASL はこちらです。

{
  "Comment": "A description of my state machine",
  "StartAt": "タスク1",
  "States": {
    "タスク1": {
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "myJobName1"
      },
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "エラー時の処理"
        }
      ],
      "Next": "タスク2"
    },
    "タスク2": {
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "myJobName2"
      },
      "Next": "タスク3",
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "エラー時の処理"
        }
      ]
    },
    "タスク3": {
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "myJobName3"
      },
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "エラー時の処理"
        }
      ],
      "End": true
    },
    "エラー時の処理": {
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "myErrorJob"
      },
      "End": true
    }
  }
}

Parallel ステートでまとめる

Parallel ステートを利用することで、Catch フィールドを一つにまとめることができます。
フロー図を見ても、先ほどと比べてすっきりしていて分かりやすい印象を受けます。

ASL はこちらです。

{
  "Comment": "A description of my state machine",
  "StartAt": "Parallel",
  "States": {
    "Parallel": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "タスク1",
          "States": {
            "タスク1": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun",
              "Parameters": {
                "JobName": "myJobName"
              },
              "Next": "タスク2"
            },
            "タスク2": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun",
              "Parameters": {
                "JobName": "myJobName"
              },
              "Next": "タスク3"
            },
            "タスク3": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun",
              "Parameters": {
                "JobName": "myJobName"
              },
              "End": true
            }
          }
        }
      ],
      "End": true,
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "エラー時の処理"
        }
      ]
    },
    "エラー時の処理": {
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "myJobName"
      },
      "End": true
    }
  }
}

注意点

こちらのパターンには、すべてのエラーを捕捉できないという大きな注意点があります。
Catch 対象のエラーに”States.ALL”を指定しても States.Runtime エラーは捕捉することができません。そのため、States.Runtime エラーが発生する可能性がある場合は、別の方法を取る必要があります。

tates.ALL での再試行またはキャッチでは States.Runtime エラーは検出されません。

Step Functions のエラー処理 – AWS Step Functions

EventBridge を利用するパターン

2021年5月に、Step FunctionsとEventBridgeとのサービス統合がサポートされました。
これにより柔軟なエラー処理を行うことが可能になりました。例えばステートマシンのステータスの変化を検知して Lambda を呼び出すことなどが可能です。
以下は EventBrdige をトリガーととする Lambda 関数を SAM デプロイする際の YAML ファイルです。
参考:EventBridgeRule – AWS Serverless Application Model

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app

  Sample SAM Template for sam-app

Globals:
  Function:
    Timeout: 3

Parameters:
  stateMachineArn:
    Type: String

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: hello_world_function/
      Handler: hello_world/app.lambda_handler
      Runtime: python3.7
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: CloudWatchEvent 
          Properties:
            Pattern:
              source:
                - aws.status
              detail-type:
                - Step Functions Execution Status Change
              detail:
                status:
                  - FAILED
                stateMachineArn:
                  - !Ref stateMachineArn

Outputs:
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

SAM デプロイに使用した TOML ファイルのサンプルです。

version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "sam-app"
s3_bucket = "{SAMアーティファクト用のS3バケット}"
s3_prefix = "sam-app"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
image_repositories = []
parameter_overrides = [
    "stateMachineArn={ステートマシンARN}}"
]

おわりに

本記事では、Step Functions 内の処理でエラーが発生したときに、特定の処理を行わせるアーキテクチャ案を紹介しました。この記事がどなたかの参考になれば幸いです。

参考

コメント