AWS Step FunctionsのChoiceステートを使って条件分岐させる

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS re:Invent 2016で発表された、ビジュアルワークフローを用いて分散アプリケーションを構築するサービス『AWS Step Functions』。

今回は Choice ステートを使って条件に応じて分岐させる方法を紹介します。

なお Step Functions が対応しているステートは以下です。

  • Task
  • Wait
  • Pass
  • Succeed
  • Fail
  • Choice
  • Parallel

サンプル 1 : Choice ステートを使った条件分岐

Step Functions の "Choice State" ブループリントです。

step-functionis-choice-blue-print

FirstState タスク のアウトプットをもとに ChoiceState の Choice によって分岐先を決定します。 具体的にはアウトプットの JSON の foo 属性の値によって

  • 整数の 1 なら "FirstMatchState"
  • 整数の 2 なら "SecondMatchState"
  • それ以外は "DefaultState"

に分岐します。

対応する Amazon States Language は以下です。

ブループリントの Amazon States Language

{
  "Comment": "An example of the Amazon States Language using a choice state.",
  "StartAt": "FirstState",
  "States": {
    "FirstState": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
      "Next": "ChoiceState"
    },
    "ChoiceState": {
      "Type" : "Choice",
      "Choices": [
        {
          "Variable": "$.foo",
          "NumericEquals": 1,
          "Next": "FirstMatchState"
        },
        {
          "Variable": "$.foo",
          "NumericEquals": 2,
          "Next": "SecondMatchState"
        }
      ],
      "Default": "DefaultState"
    },

    "FirstMatchState": {
      "Type" : "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:OnFirstMatch",
      "Next": "NextState"
    },

    "SecondMatchState": {
      "Type" : "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:OnSecondMatch",
      "Next": "NextState"
    },

    "DefaultState": {
      "Type": "Fail",
      "Cause": "No Matches!"
    },

    "NextState": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
      "End": true
    }
  }
}

Lambda 関数の実装

FirstState タスクの Lambda 関数に { "foo" : 0 OR 1 OR 2 のいずれか}" を返すものを用意します。

import random
def lambda_handler(event, context):
    return {"foo":random.choice([0,1,2])}

また

  • FirstMatchState
  • SecondMatchState

の Lambda 関数も、各 Lambda 関数を経由したことがわかるように input に経由した関数名を含めるように一部加工します。

FirstMatchState Lambda Function

def lambda_handler(event, context):
    event["func_name"] = "onFirstMatch"
    return event

SecondMatchState Lambda Function

def lambda_handler(event, context):
    event["func_name"] = "onSecondMatch"
    return event

(修正版)ブループリントの Amazon States Language

Task のリソース ARN を書き換えたのが次の Amazon States Language です。

NextState ステートは Lambda 関数の実装をサボり、input をそのまま output する Pass タイプにしています。

{
  "Comment": "An example of the Amazon States Language using a choice state.",
  "StartAt": "FirstState",
  "States": {
    "FirstState": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:eu-west-1:12345:function:OnFirstState",
      "Next": "ChoiceState"
    },
    "ChoiceState": {
      "Type" : "Choice",
      "Choices": [
        {
          "Variable": "$.foo",
          "NumericEquals": 1,
          "Next": "FirstMatchState"
        },
        {
          "Variable": "$.foo",
          "NumericEquals": 2,
          "Next": "SecondMatchState"
        }
      ],
      "Default": "DefaultState"
    },

    "FirstMatchState": {
      "Type" : "Task",
      "Resource": "arn:aws:lambda:eu-west-1:12345:function:OnFirstMatch",
      "Next": "NextState"
    },

    "SecondMatchState": {
      "Type" : "Task",
      "Resource": "arn:aws:lambda:eu-west-1:12345:function:OnSecondMatch",
      "Next": "NextState"
    },

    "DefaultState": {
      "Type": "Fail",
      "Cause": "No Matches!"
    },

    "NextState": {
      "Type": "Pass",
      "End": true
    }
  }
}

実行例

ステートマシン作成後は "New execution" から実行します。

step-functions-new-execution-button

FirstState ステートは Input に依存しないため、デフォルトのインプットのままステートマシンを実行します。

実行例1

secondmatchstate-output

FirstState が

{"foo": 2}

というアウトプットだったため、ChoiceState から SecondMatchState に遷移し、

{"func_name": "onSecondtMatch", "foo": 2}

をアウトプットしています。

実行例2

default-state-output

FirstState が

{"foo": 0}

というアウトプットだったため、ChoiceState から DefaultState に遷移し、Fail タイプによる例外が発生しています。

Choice ステートを使った条件分岐 : サンプル2

Step Functions の Developer マニュアルの Choice ステートのサンプルです。

ChoiceTwenties

Start 時のインプットをもとに ChoiceStateX の Choice によって分岐先を決定します。 具体的にはインプットの JSON の type 属性 と value 属性の値によって

  • type が "Public" なら "FirstMatchState"
  • type が "Private" なら
    • value == 0 なら ValueIsZero
    • 20 <= value < 30 なら ValueInTwenties
  • それ以外は "DefaultState"

に分岐します。

対応する Amazon States Language は以下です。

ブループリントの Amazon States Language

{
  "Comment": "Choice",
  "StartAt": "ChoiceStateX",
  "States": {
    "ChoiceStateX": {
      "Type": "Choice",
      "Choices": [
        {
          "Not": {
            "Variable": "$.type",
            "StringEquals": "Private"
          },
          "Next": "Public"
        },
        {
          "Variable": "$.value",
          "NumericEquals": 0,
          "Next": "ValueIsZero"
        },
        {
          "And": [
            {
              "Variable": "$.value",
              "NumericGreaterThanEquals": 20
            },
            {
              "Variable": "$.value",
              "NumericLessThan": 30
            }
          ],
          "Next": "ValueInTwenties"
        }
      ],
      "Default": "DefaultState"
    },
    "Public": {
      "Type": "Pass",
      "Result": "Public",
      "End": true
    },
    "ValueIsZero": {
      "Type": "Pass",
      "Result": "ValueIsZero",
      "End": true
    },
    "ValueInTwenties": {
      "Type": "Pass",
      "Result": "ValueInTwenties",
      "End": true
    },
    "DefaultState": {
      "Type": "Fail",
      "Cause": "No Matches!"
    }
  }
}

「20台の整数であること」はオペレーター

  • And
  • NumericGreaterThanEquals
  • NumericLessThan

を組み合わせて「20以上かつ30未満」というようにして実現しています。

        {
          "And": [
            {
              "Variable": "$.value",
              "NumericGreaterThanEquals": 20
            },
            {
              "Variable": "$.value",
              "NumericLessThan": 30
            }
          ],
          "Next": "ValueInTwenties"
        }

実行例

ステートマシン作成後は "New execution" から実行します。 うまく分岐するようにインプットを用意します。

実行例1

twenty-private-execution

{
  "type": "Private",
  "value": 20
}

というインプットに対しては ChoiceStateX から ValueInTwenties に遷移して

"ValueInTwenties"

をアウトプットしています。

実行例2

twenty-public-execution

{
  "type": "Public",
  "value": 20
}

というインプットに対して ChoiceStateX から Public に遷移して

"Public"

をアウトプットしています。

Choice タイプが対応しているオペレーター

今回のサンプルでは、条件に一致されるためのオペレーターとして

  • NumericEquals
  • NumericGreaterThanEquals
  • And
  • StringEquals

等を利用しました。

現時点では、以下のオペレーターに対応しています。

  • StringEquals
  • StringLessThan
  • StringGreaterThan
  • StringLessThanEquals
  • StringGreaterThanEquals
  • NumericEquals
  • NumericLessThan
  • NumericGreaterThan
  • NumericLessThanEquals
  • NumericGreaterThanEquals
  • BooleanEquals
  • TimestampEquals
  • TimestampLessThan
  • TimestampGreaterThan
  • TimestampLessThanEquals
  • TimestampGreaterThanEquals
  • And
  • Or
  • Not

まとめ

今回は Choice ステートによる条件分岐方法を紹介しました。

String/Numeric/Boolean/Timestamp の比較や And/Or の組み合わせも可能なため、Amazon States Language だけでも複雑な条件を記述することができます。 一方で、ステートマシンの記述言語は JSON ベースなため、なんでも JSON で記述すると、メンテナンスが大変になります。

JSON と Lambda タスクで上手くバランスを取りながらステートを記述することが求められます。

参考