[アップデート]Step Functions:動的タイムアウトや組み込み関数などが追加されASLが強化されました

ASLの拡張アップデートいっぱいきた
2020.08.15

ステートマシンを定義する際に利用するAmazon States Language(以下、ASL)にアップデートがありました。

今回のアップデートで動的タイムアウトや組み込み関数などがサポートされ、ASLがより強化されました。それぞれみていきたいと思います。

アップデート

演算子追加

Choiceステート内で利用できる演算子に以下が加わりました。

  • IsNull … パス(以下、変数)の値がNULLかどうかの確認
  • IsString … 変数の値が文字列かどうかの確認
  • IsNumeric … 変数の値が数値かどうかの確認
  • IsBoolean … 変数の値がブール値かどうかの確認
  • IsTimestamp … 変数の値がタイムスタンプかどうかの確認
  • IsPresent … 変数の存在確認
  • StringMatches … 変数の値のパターンが一致するかどうかの確認(ワイルドカード利用可)
  • StringEqualsPath … 変数と変数の値が一致するかどうかの確認

例えば、以下のステートではCommentが存在していることかつ、値が文字列の場合は条件を満たし次のステートに遷移します。(Choice ルールに従い状態が遷移します)

"State1": {
  "Type": "Choice",
  "Choices": [
    {
      "And": [
        {
          "Variable": "$.Comment",
          "IsPresent": true
        },
        {
          "Variable": "$.Comment",
          "IsString": true
        }
      ],
      "Next": "State2"
    }
  ],
  "Default": "State3"
}

IsPresentを利用することで、存在しない変数にアクセスした際のエラーを防ぐことができます。

Contextオブジェクトへのグローバルアクセス

Context オブジェクトは、ステートマシンの実行名など、ステートマシンの実行に関する情報が含まれています。 アップデート以前はParametersフィールド内からのみアクセス可能でしたが、こちらの制限がなくなり、以下のようにParametersフィールド外からもアクセスが可能になりました。

"Check Context Object": {
  "Type": "Choice",
  "Choices": [
    {
      "Variable": "$$.Execution.Name",
      "IsString": true,
      "Next": "Check Context Object"
    }
  ],
  "Default": "Fail"
}

ResultSelectorフィールド追加

Lambda Functionなど、Step Functionsサービスに統合されたAWSサービスを呼び出す際に、結果に加えてメタデータを返すことがあります。 例えば、以下のようにLambda Functionを呼び出すとメタデータも返されます。

ASL定義

"State1":{
  "Type":"Task",
  "Resource":"arn:aws:states:::lambda:invoke",
  "Parameters":{
    "FunctionName":"arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:Funtion",
  },
  "ResultSelector": {
    "RequestId.$": "$.SdkResponseMetadata.RequestId",
    "StatusCode.$": "$.StatusCode"
  },
  "ResultPath": "$.InvokeResult",
  "End":true
}

メタデータ例

{
  "Payload": null,
  "SdkHttpMetadata": {
    "AllHttpHeaders": {
      "x-amzn-Remapped-Content-Length": [
        "0"
      ],
      "Connection": [
        "keep-alive"
      ],
      "x-amzn-RequestId": [
        "2c17a67a-c238-4fb0-a32f-9524741df09f"
      ],
      "Content-Length": [
        "0"
      ],
      "Date": [
        "Fri, 14 Aug 2020 10:48:44 GMT"
      ],
      "X-Amzn-Trace-Id": [
        "root=1-5f366c0c-e34f89000bc9c900e0b4f400;sampled=0"
      ]
    },
    "HttpHeaders": {
      "Connection": "keep-alive",
      "Content-Length": "0",
      "Date": "Fri, 14 Aug 2020 10:48:44 GMT",
      "x-amzn-Remapped-Content-Length": "0",
      "x-amzn-RequestId": "2c17a67a-c238-4fb0-a32f-9524741df09f",
      "X-Amzn-Trace-Id": "root=1-5f366c0c-e34f89000bc9c900e0b4f400;sampled=0"
    },
    "HttpStatusCode": 202
  },
  "SdkResponseMetadata": {
    "RequestId": "2c17a67a-c238-4fb0-a32f-9524741df09f"
  },
  "StatusCode": 202
}

ResultSelectorを利用することで結果の一部を選択し、それらを出力することが可能です。上記ASL定義の場合、以下のような出力となります。

"InvokeResult": {
  "RequestId": "2c17a67a-c238-4fb0-a32f-9524741df09f"
  "StatusCode": 202
}

組み込み関数追加

Taskなしでリテラル値の操作が行える、以下の組み込み関数が追加されました。

  • States.Format … フォーマット(文字結合など)
  • States.StringToJson … 文字列からJSONに変換
  • States.JsonToString … JSONから文字列に変換
  • States.Array … 0個以上の引数を取得

以下、JsonToStringの使用例です。

ASL定義

"State1": {
  "Type": "Pass",
  "Parameters": {
    "result.$": "States.JsonToString($.test)"
  },
  "End": true
}

入力

{
  "test": {
     "foo": "bar"
  }
}

出力

{
  "result": "{\"foo\":\"bar\"}"
}

なお、組み込み関数が利用可能なフィールドは制限がありますのでご注意ください。

動的タイムアウト

以前からTimeoutSecondsでTaskのタイムアウト指定や、Activity利用時のタイムアウト(ハートビート間隔)を指定するHeartbeatSecondsは存在していましたが、数値を指定する必要があり、動的な値を定義することはできませんでした。

今回のアップデートによりTimeoutSecondsPathHeartBeatSecondsPathがサポートされ、変数で値を指定することが可能になりました。例えば、以下のようにState1ステートで実行されるLambda Functionでタイムアウトの時間を生成し($.wait_timeに格納)、後続のState2ステートでタイムアウトを指定することが可能になります。

"State1": {
  "Type": "Task",
  "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:wait_time_generation",
  "ResultPath": "$.wait_time",
  "Next": "State2"
},
"State2": {
  "Type": "Task",
  "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:WaitFuntion",
  "TimeoutSecondsPath": "$.wait_time",
  "End": true
}

やってみた

上記で紹介したをアップデートをピックアップした、以下のステートマシンを作成してみました。

ASL定義

{
  "StartAt":"Check Input",
  "States":{
    "Check Input": {
      "Type": "Choice",
      "Choices": [
        {
          "And": [
            {
              "Variable": "$.Comment",
              "IsPresent": true
            },
            {
              "Variable": "$.Comment",
              "StringMatches": "N*n"
            }
          ],
          "Next": "Check Context Object"
        }
      ],
      "Default": "Fail"
    },
    "Check Context Object": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$$.Execution.Name",
          "IsString": true,
          "Next": "Invoke Event Lambda"
        }
      ],
      "Default": "Fail"
    },
    "Invoke Event Lambda":{
      "Type":"Task",
      "Resource":"arn:aws:states:::lambda:invoke",
      "Parameters":{
        "FunctionName": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:wait_processing",
        "InvocationType" : "Event"
      },
      "ResultSelector": {
        "RequestId.$": "$.SdkResponseMetadata.RequestId",
        "StatusCode.$": "$.StatusCode"
      },
      "ResultPath": "$.InvokeResult",
      "Next": "Text Format"
    },
    "Text Format": {
      "Type": "Pass",
      "Parameters": {
        "Result.$": "States.Format('Cat: {} Dog : {}', $.Comment, $$.Execution.Name)"
      },
      "Next": "Wait Time Generation"
    },
    "Wait Time Generation": {
      "Type": "Task",
      "Resource":  "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:wait_time_generation",
      "ResultPath": "$.wait_time",
      "Next": "IsNumeric Wait Time"
    },
    "IsNumeric Wait Time": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.wait_time",
          "IsNumeric": true,
          "Next": "Exec Processing"
        }
      ],
      "Default": "Fail"
    },
    "Exec Processing": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:wait_processing",
      "TimeoutSecondsPath": "$.wait_time",
      "Next": "Succeed"
    },
    "Succeed": {
      "Type": "Succeed"
    },
    "Fail": {
      "Type": "Fail"
    }
  }
}

ビジュアルワークフロー

ASL定義について説明します。

Check Input

追加された演算子IsPresentを利用して、Commentの存在を確認しています。(Commentは実行時に入力する想定)存在した場合に限りStringMatchesで変数内の値のパターン(ここでは、Nから始まりnで終わる)をチェックします。Choiceタイプのステートのため、チェック結果に応じ処理が分岐されます。

Check Context Object

Contextオブジェクトへのグローバルアクセスの確認ため、Parametersフィールド外からステートマシンの実行名を取得し、IsStringで値が文字列かチェックしています。

Invoke Event Lambda

ここではLambda Functionを非同期で呼び出し、ResultSelectorフィールドでメタデータの一部を取得しています。

Text Format

組み込み関数States.Formatを利用し、実行時に入力する想定のCommentと、Contextオブジェクトから実行名を取得し結合しています。

Wait Time Generation

後続のTaskステートで動的なタイムアウトを設けたいので、Lambda Functionを呼び出しタイムアウト時間を生成しています。

IsNumeric Wait Time

Wait Time Generationステートで出力した値をIsNumericで数値かどうか確認しています。

Exec Processing

TimeoutSecondsPathでTaskのタイムアウトを変数でいます。意図的にTaskの実行時間より短いタイムアウトを値を設定しています。

ここでは、このステートでエラー終了すれば想定通りの動作となります。

実行

start-executionで、作成したステートマシンを実行します。

$ SFN_ARN=<作成したステートマシンのARN>
$ aws stepfunctions start-execution \
    --state-machine-arn ${SFN_ARN}  \
    --name "pochi" \
    --input "{\"Comment\" : \"Nochan\"}"
{
    "executionArn": "arn:aws:states:ap-northeast-1:xxxxxxxxxxxx:execution:morimori_update_sfn:pochi",
    "startDate": "2020-08-15T11:28:18.071000+09:00"
}

しばらくすると、想定どおりの結果となりました。

結果についてみていきたいと思います。

Check Input

入力値のCommentで、Nから始まりnで終わるパターンNochanを指定したので、Check Inputステートは成功しました。

Check Context Object

実行名pochiを指定していたので、Check Context Objectも成功です。

Invoke Event Lambda

メタデータの一部が取得できました。

Text Format

値の結合ができました。

Wait Time Generation

Lambda Functionからタイムアウト値を生成しました。

IsNumeric Wait Time

Wait Time Generationステートで数値を出力していたのでステートは成功です。

Exec Processing

動的に生成したタイムアウト値(ここでは60秒)で、タイムアウトが発生しステートが失敗しました。

確認は以上です。

今回利用した環境を、AWS SAMテンプレートでアップしておいたので、手元で試してみたい方はご利用ください。

さいごに

今回のアップデートにより、Taskで実施していた処理をASL側に寄せることができ、より簡素なワークフローが作成しやすくなったと思います。まだ日本語のドキュメントは更新されていないようですので、公式ドキュメントを確認する際は、英語のドキュメントを確認ください。

参考