変数を使用したステート間のデータ受け渡し
変数と JSONata によるステート管理
Step Functions では最近、ステート管理とデータ変換のために変数と JSONata が追加されました。
詳細については、ブログ記事「Simplifying developer experience with variables and JSONata in AWS Step Functions
次のビデオでは、Step Functions の変数と JSONata を DynamoDB の例で説明しています。
変数とステートの出力を使用すると、ワークフローのステップ間でデータを受け渡すことができます。
ワークフロー変数を使用すると、あるステップでデータを保存し、後のステップでそのデータを取得できます。例えば、後で必要になる可能性のあるデータを含む API レスポンスを保存できます。逆に、ステートの出力は、直後のステップへの入力としてのみ使用できます。
変数の概念的概要
ワークフロー変数を使用すると、後で参照するためにデータを保存できます。例えば、ステップ 1 で API リクエストの結果を保存し、それを後のステップ 5 で再利用できます。
次のシナリオでは、ステートマシンは API からデータを 1 回取得します。ステップ 1 で、ワークフローは返された API データ (ステートごとに最大 256 KiB) を変数「x」に保存し、後続のステップで使用できるようにします。
変数を使用せずに同じことを行おうとすると、ステップ 1 → 2 → 3 → 4 → 5 というすべてのステップでデータを渡す必要があります。それらの中間ステップでそのデータが必要ない場合はどうなるでしょうか。出力と入力を通じてステートからステートへデータを渡すことは無駄な処理となります。
変数を使用すると、一度データを保存しておけば、後続のどのステップからでもそのデータにアクセスできます。データフローを中断することなく、ステップを変更、再配置、または追加することもできます。変数の柔軟性を活かせば、Output の使用は、Parallel や Map のサブワークフローからデータを返すときや、ステートマシンの実行終了時のみで済むようになります。
変数をサポートするステート
Pass、Task、Map、Parallel、Choice、Wait のステートタイプでは、Assign を使用して変数の宣言と値の代入ができます。
変数を設定するには、変数の名前と値を含む JSON オブジェクトを指定します。
"Assign": {
"productName": "product1",
"count" : 42,
"available" : true
}
変数を参照するには、名前の前にドル記号 ($) を付けます (例: $productName)。
予約変数: $states
Step Functions では、$states という名前の予約変数が定義されています。JSONata のステートでは、次の構造が JSONata の式用に $states に割り当てられます。
# Reserved $states variable in JSONata states
$states = {
"input": // Original input to the state
"result": // API or sub-workflow's result (if successful)
"errorOutput": // Error Output (only available in a Catch)
"context": // Context object
}
ステートの開始時に、Step Functions ではステートの入力が $states.input に割り当てます。$states.input の値は、JSONata 式を受け入れるすべてのフィールドで使用できます。$states.input は常にステートの入力の元の値を参照します。
Task、Parallel、および Map ステートの場合:
-
API またはサブワークフローが成功した場合、
$states.resultはその未加工の結果を参照します。 -
API またはサブワークフローが失敗した場合、
$states.errorOutputはそのエラー出力を参照します。$states.errorOutputはCatchフィールドのAssignまたはOutputで使用できます。
アクセスできないステートやフィールドで $states.result や $states.errorOutput にアクセスしようとすると、ステートマシンの作成、更新、検証時にキャッチされます。
$states.context オブジェクトは、StartTime、タスクトークン、初期ワークフロー入力など、特定の実行に関する情報をワークフローに提供します。詳細についてはStep Functions の Context オブジェクトから実行データにアクセスする を参照してください。
変数名の構文
変数名は、Unicode® Standard Annex #31
変数名の規則は JavaScript などのプログラミング言語のルールと似ています。
変数のスコープ
Step Functions ワークフローは、workflow-local スコープを使用して、変数との競合状態を回避します。
ワークフローローカルスコープには、ステートマシンの States フィールド内のすべてのステートが含まれますが、Parallel または Map ステート内のステートは含まれません。Parallel または Map ステート内のステートは外側のスコープの変数を参照できますが、それぞれ独立したワークフローローカル変数と値を作成して保持します。
Parallel ブランチと Map イテレーションは、外側のスコープの変数値にはアクセスできますが、他の同時実行中のブランチやイテレーションの変数値にはアクセスできません。エラー処理時、Catch 内の Assign フィールドは、外側のスコープ (Parallel/Map ステートが存在するスコープ) にある変数に値を割り当てることができます。
例外: Distributed Map ステートは、現在のところ外側のスコープにある変数を参照できません。
スコープ内のいずれかのステートが変数に値を割り当てると、その変数はスコープ内に存在します。よくあるエラーを避けるために、内側のスコープで割り当てられた変数は、外側のスコープで割り当てられた変数と同じ名前にすることはできません。例えば、トップレベルのスコープで myVariable という変数に値を割り当てている場合、他のスコープ (Map や Parallel の中) で同じ myVariable に値を割り当てることはできません。
変数へのアクセスは現在のスコープによって決まります。Parallel および Map ステートには独自のスコープがありますが、外側のスコープの変数にはアクセスできます。
Parallel または Map ステートが完了すると、それらのスコープ内の変数はスコープ外となり、アクセスできなくなります。Parallel のブランチやマップのイテレーションからデータを渡すには、Output フィールドを使用します。
ASL の Assign フィールド
ASL の Assign フィールドは、1 つ以上の変数に値を割り当てるために使用されます。Assign フィールドは、各ステートのトップレベル (Succeed と Fail を除く)、Choice ステートのルール内、および Catch フィールド内で使用できます。例:
# Example of Assign with JSONata
"Store inputs": {
"Type": "Pass",
"Next": "Get Current Price",
"Comment": "Store the input desired price into a variable: $desiredPrice",
"Assign": {
"desiredPrice": "{% $states.input.desired_price %}",
"maximumWait": "{% $states.input.max_days %}"
}
},
Assign フィールドは JSON オブジェクトを受け取ります。トップレベルの各フィールドが、割り当て先となる変数の名前になります。前の例では、変数名は desiredPrice と maximumWait です。JSONata を使用する場合、{% ... %} は JSONata 式を示し、これには変数やより複雑な式が含まれることがあります。JSONata 式の詳細については、JSONata.org のドキュメント
次の図では、JSONata をクエリ言語として使用する場合、Assign と Output のフィールドが並行して処理される方法を示しています。重要: 変数に値を割り当てても、ステートの Output には影響しません。
次の JSONata の例では、ステートの入力から order.product を取得しています。変数 currentPrice には、タスクの結果から取得した値が割り当てられます。
# Example of Task with JSONata assignment from result
{
"Type": "Task",
...
"Assign": {
"product": "{% $states.input.order.product %}",
"currentPrice": "{% $states.result.Payload.current_price %}"
},
"Next": "the next state"
}
注: 変数の一部分に値を割り当てることはできません。例えば、"Assign":{"x":42} はできますが、"Assign":{"x.y":42} や "Assign":{"x[2]":42} はできません。
Assign フィールド内での評価順序
Step Functions では、ステート内のすべての変数参照は、ステート開始時の値を使用します。
この前提は、Assign フィールドが複数の変数にどのように値を代入するかを理解するために重要です。まず、新しい値が計算されます。次に Step Functions が新しい値を変数に割り当てます。新しい変数値は、次のステートから使用できるようになります。例えば、次のような Assign フィールドを考えてみましょう。
# Starting values: $x=3, $a=6
"Assign": {
"x": "{% $a %}",
"nextX": "{% $x %}"
}
# Ending values: $x=6, $nextX=3
前の例では、変数 x は割り当てと同時に参照もされています。
すべての式はまず評価されてから、割り当てられることに注意してください。その後、新しく割り当てられた値は次のステートで使用できるようになります。
この例を詳しく見ていきましょう。前のステートで、$x には 3 が割り当てられ、$a には 6 が割り当てられたとします。このプロセスのステップを以下に示します。
-
すべての式は、すべての変数の現在の値を使用して評価されます。
式
"{% $a %}"は 6 に評価され、"{% $x %}"は 3 に評価されます。 -
次に、割り当てが行われます。
$xに値 6 が割り当てられます。$nextXに値 3 が割り当てられます。
注: $x が事前に割り当てられていなかった場合、$x が未定義となるため、この例は失敗します。
まとめると、Step Functions はすべての式を評価してから、割り当てを行います。Assign フィールド内での変数の出現順序は関係ありません。
制限
1 つの変数の最大サイズは、Standard ワークフローと Express ワークフローの両方で 256 KiB です。
1 つの Assign フィールド内のすべての変数の合計サイズも最大 256 KiB です。例えば、X と Y に 128 KiB を割り当てることはできますが、同じ Assign フィールドで X と Y の両方に 256 KiB を割り当てることはできません。
1 回の実行で保存できるすべての変数の合計サイズは 10MiB を超えることはできません。
JSONPath ステートでの変数の使用
変数は、クエリ言語に JSONPath を用いるステートでも使用できます。
変数は、JSONPath 式 ($. または $$. 構文) を受け入れる任意のフィールドで参照できます。ただし、ResultPath フィールドは例外であり、これはステートの結果をステートの入力内のどこに挿入するかを指定します。変数は ResultPath では使用できません。
JSONPath では、$ 記号は「現在」の値を参照し、$$ はステートの Context オブジェクトを表します。JSONPath 式は $.customer.name のように $. で始めることができます。$$.Execution.Id のように $$. を使用してコンテキストにアクセスできます。
変数を参照するには、変数名の前に $ 記号を付けて $x や $order.numItems のように記述します。
組み込み関数を受け入れる JSONPath フィールドでは、引数で変数を使用できます (例: States.Format('The order number is {}', $order.number))。
次の図では、JSONPath タスクの Assign ステップが ResultSelector と同時に発生する方法を示しています。
JSONPath での変数割り当て
JSONPath での変数割り当てはペイロードテンプレートの動作と似ています。.$ で終わるフィールドは JSONPath 式を表しており、Step Functions がステートマシンの実行時に値へと評価します (例: $.order..product や $.order.total)。
# Example of Assign with JSONPath
{
"Type": "Task",
...
"Assign": {
"products.$": "$.order..product",
"orderTotal.$": "$.order.total"
},
"Next": "the next state"
}
JSONPath のステートでは、Assign フィールド内の $ の値はステートタイプによって決まります。Task,、Map、Parallel ステートでは、$ は API/サブワークフローの結果を指します。Choice および Wait ステートでは、$ は InputPath 適用後のステートの入力、つまり有効な入力を指します。Pass では、$ は、Result フィールドによって生成されたか、InputPath/Parameters フィールドによって生成されたかにかかわらず、結果を指します。
次の JSONPath の例では、JSON オブジェクトを details 変数に割り当て、JSONPath 式 $.result.code の結果を resultCode に、JSONPath 式 States.Format('Hello {}', $customer.name) の結果を message に割り当てています。これが Task ステート内であれば、$.order.items および $.result.code 内の $ は API の結果を指します。startTime 変数には、Context オブジェクト $$.Execution.StartTime から取得した値が割り当てられます。
"Assign": {
"details": {
"status": "SUCCESS",
"lineItems.$": "$.order.items"
},
"resultCode.$": "$.result.code",
"message.$": "States.Format('Hello {}', $customer.name)",
"startTime.$": "$$.Execution.StartTime"
}