🦭

AWS Step Functionsで同期的に配列をループする

2024/03/20に公開

はじめに

Step Functionsでの、配列をイテレータとする同期的なループ処理の実装方法について解説します。

処理全体の概要とコード


![[step-functions-sync-loop.png]]

  "Comment": "Eaxmaple of Syne Iteration",
  "StartAt": "Input Array",
  "States": {
    "Input Array": {
      "Type": "Pass",
      "Result": {
        "Steps": [
          0,
          1,
          2,
          "DONE"
        ],
        "Items": [
          "foo",
          "bar",
          "baz"
        ]
      },
      "Next": "Choice"
    },
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.Steps[0]",
          "StringEquals": "DONE",
          "Next": "Success"
        }
      ],
      "Default": "Set Iter Item"
    },
    "Set Iter Item": {
      "Type": "Pass",
      "Next": "Run Job(Wait)",
      "Parameters": {
        "Steps.$": "$.Steps",
        "Items.$": "$.Items",
        "IterItem.$": "States.ArrayGetItem($.Items, $.Steps[0])"
      }
    },
    "Run Job(Wait)": {
      "Type": "Wait",
      "Seconds": 1,
      "Next": "Pop Iter Item",
      "Comment": "Nothing to do here. Instead of this, enter you job."
    },
    "Pop Iter Item": {
      "Type": "Pass",
      "Next": "Choice",
      "Parameters": {
        "Steps.$": "$.Steps[1:]",
        "Items.$": "$.Items"
      }
    },
    "Success": {
      "Type": "Succeed"
    }
  }
}

inputの入力

私はStepFunctionsにすべてのパラメータを置くことが多く、今回もPassステートにインプットする配列を記入しました。Itemsの配列をイテレータとして、ループを実行することを想定しています。また、ループ処理を実装するための手段として、配列の位置をStepsに入力しています。Stepsの最後の要素を"DONE"としており、これをループ終了の判定に用います。

 "States": {
    "Input Array": {
      "Type": "Pass",
      "Result": {
      # イテレータの配列を指定
        "Steps": [
          0,
          1,
          2,
          "DONE"
        ],
        "Items": [
          "foo",
          "bar",
          "baz"
        ]
      },
      "Next": "Choice"
    }

IterationするItemの保存

Set Iter Itemと書かれたPassステートにおける入力Parametersのフィールドで、組み込み関数であるStates.ArrayGetItemを使って、Itertaionする要素を取得します。後続するステートでそのまま取得しても良いのですが、場合によっては関数を使えないステートもあるかもしれず、わかりやすさも兼ねて、このようにPassステートを使用しています。

なお、配列から要素の選択する際に、$.Items[$.Steps[0]]と指定すると、値が更新されないことがありました。組み込み関数であるStates.ArrayGetItemを使ったほうがよいのではないかと思います。

    "Set Iter Item": {
      "Type": "Pass",
      "Next": "Run Job(Wait)",
      "Parameters": {
        "Steps.$": "$.Steps",
        "Items.$": "$.Items",
        "IterItem.$": "States.ArrayGetItem($.Items, $.Steps[0])"
      }
    },

Iterationが終わったItemの削除

Passステートの入力Parametersフィールドを使って、情報を更新します。Stepsの0番目の要素を番号とする配列をItemsから取得しておりましたので、取得が終わったStepsの要素を削除します。$.Steps[1:]のようにして、配列から先頭の要素を削除します。

    "Pop Iter Item": {
      "Type": "Pass",
      "Next": "Choice",
      "Parameters": {
        "Steps.$": "$.Steps[1:]",
        "Items.$": "$.Items"
      }
    },

Choice ステートによるループの終了判定

$.Stepsの先頭が"DONE"であれば、ループを終了するようにします。つまり、$.Stepsの先頭行の情報を使って配列から要素を取得し、ループごとに先頭行を更新していくことで、ループを正常に終了させています。

"Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.Steps[0]",
          "StringEquals": "DONE",
          "Next": "Success"
        }
      ],
      "Default": "Set Iter Item"
    },

おわりに

個人的にはパラメータをStep Funcstions内にjsonとしてハードコードするのが好みですので、この方法は便利であるかと思います。一方で、Stepsのように、配列の位置に関する情報を追記しないといけないのはデメリットになるかと思います。

参考にさせていただいた記事

Step FunctionsのParameterを使って上限ありのループを作る
AWS StepFunctionsで一定回数ループする

Discussion