Slide 1

Slide 1 text

AWS Step Functions 大規模並列の壁を越える JAWS-UG新潟支部 笠原 宏 #jawsug #jawssonic2026

Slide 2

Slide 2 text

自己紹介 笠原 宏 (@kasacchiful) クラスメソッド株式会社 データ事業本部 ビジネスソリューション部 ソリューションアーキテクト 新潟県新潟市在住 JAWS-UG新潟 / Python機械学習勉強会 in 新潟 / JaSST Niigata / ASTER / SWANII / Cloudflare Meetup Niigata / AI CRAFT Hacks Niigata / KomeKaigi AWS Community Builder (Serverless) 2022, 2025 Japan AWS Top Engineer / 2022-2026 Japan All AWS Certifications Engineer AWS Samurai 2025 2

Slide 3

Slide 3 text

今日のお話 AWS Step Functionsを使って、大規模並列実行する際の心構え 1. イベント履歴 25,000 の壁と、分割ワークフローでの回避 2. 分割したワークフロー間での、データの受け渡し 3. Mapステートで並列数を上げきる 4. 並列数を上げた先で考慮すべきこと 3

Slide 4

Slide 4 text

クイズ Lambda を呼んで、Choice で判定して、まだなら戻る。 それだけのループです。 Standard ワークフロー (ステートマシン) の最大実行時 間は1年です。 さて、Lambda関数は何回ループ実行され るでしょうか? A. 約25,000回 B. 約5,000回 C. 約3,500回 D. 上限なし(1年動き続ける) 4

Slide 5

Slide 5 text

答え: C. 約3,500回 実際に実行すると、3,571回まで実行するとエ ラーになります。 1年どころか、6分足らずで落ちます。 何が原因でしょうか? 5

Slide 6

Slide 6 text

1. イベント履歴 25,000 の壁と、分割ワークフローでの回避

Slide 7

Slide 7 text

最大実行イベント履歴 25,000 イベント Standard Express 最大実行時間 1年 5分 最大実行履歴サイズ 25,000イベント 無制限 イベント履歴の上限を超えるとステートマ シンの実行が失敗します 上限解放不可 (ハードクォータ) Express は無制限 履歴を Step Functions 側に持たず CloudWatch Logs で持つため https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/service-quotas.html 7

Slide 8

Slide 8 text

イベント履歴 24,999 からの挙動 公式ドキュメントより AWS Step Functionsの実行イベント履歴には 25,000 エントリのハードクォータがあり ます。実行イベントが 24,999 件に達すると、次のイベントが発生するまで待機しま す。 イベント番号 25,000 が ExecutionSucceeded の場合、実行は正常に終了します。 イベント番号 25,000 が ExecutionSucceeded 以外の場合、ExecutionFailed イベント はログに記録され、履歴の上限に達したためステートマシンの実行は失敗します。 https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/sfn-best-practices.html 8

Slide 9

Slide 9 text

状態遷移と実行イベント {% $count < $iterations %} 状態遷移 実⾏イベント Start Pass state Task state (Lambda Invoke) Choice state ExecutionStarted PassStateEntered TaskStateEntered ChoiceStateEntered PassStateExited LambdaFunctionScheduled ChoiceStateExited Succed state End ExecutionFailed LambdaFunctionStarted LambdaFunctionsSucceeded TaskStateExited ループは Lambda関数実行のTaskステートとChoiceステートを順に繰り返したので、 1ループあたり「2つの状態遷移」/「7つの実行イベント」が発生。 Starndardワークフローの課金対象は「状態遷移数」(今回は 7145 ) なので、実行イベントは あまり気にしないことが多いが、大規模実行の場合は実行イベントも気にする必要がある。 9

Slide 10

Slide 10 text

実行結果 Start 1イベント × 1回 ( ExecutionStart ) EventHistoryInit (Pass state) 2イベント × 1回 EventHistoryInvokeCounter (Lambda Invoke) 5イベント × 3571回 EventHistoryCheckDone (Choice state) 2イベント × 3570回 + 1イベント 24999イベント目が ChoiceStateEntered End 1イベント × 1回 ( ExecutionFailed ) 実行イベント: 1+2+(5×3571)+(2×3570+1)+1 = 25000 10

Slide 11

Slide 11 text

回避方法 — ステートマシンの実行を分割する 履歴は「ステートマシンの実行単位」でカウントされる。 つまり、ステートマシンの実行を分ければ回避可能。 ステートマシンの実行を数珠繋ぎで ステートマシンの実行を入れ子で 公式ドキュメント上でも以下の記載があり、この方法を推奨している。 ワークフローをより小さなステートマシンに分割し、進行中の作業を新しい実行で継続 する必要があります。 入れ子構造で分割・実行するケースが多いので、以降「分割 = 入れ子」で記載します。 https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-continue-new.html 11

Slide 12

Slide 12 text

入れ子の書き方 { "Type": "Task", "Resource": "arn:aws:states:::states:startExecution.sync:2", "Arguments": { "StateMachineArn": "arn:aws:states:...:stateMachine:ChildWorkflow", "Input": { "AWS_STEP_FUNCTIONS_STARTED_BY_EXECUTION_ID": "{% $states.context.Execution.Id %}" } } } Resource startExecution startExecution.sync startExecution.sync:2 挙動 起動して即座に次へ。完了を待たない 完了を待つ。結果は「エスケープされた文字列」 完了を待つ。結果は「パース済み JSON」 入れ子したい部分をTaskステートで子ステートマシンを呼び出す。同期実行であれば、結果 を親ステートマシン側に返せる。 12

Slide 13

Slide 13 text

親子をリンクさせる AWS_STEP_FUNCTIONS_STARTED_BY_EXECUTION_ に親の実行IDを付与すると、コンソールで 「親から子の実行ステートマシンへ」リンク が張られます。 親の実行IDを付与しなくても動作するが、付 与しないとこのリンクは出ない CDK なら associateWithParent: true で 自動付与可能 ステートマシンを分割すると追跡が難しく なるので、必ず付与したい ID 13

Slide 14

Slide 14 text

2. 分割したワークフロー間での、データの受け渡し

Slide 15

Slide 15 text

ワークフロー分割後に考える問題 - データの受け渡し ワークフロー分割した後、今度はワークフロー間のデータの受け渡しを考える。 子ワークフローに何を渡し、子ワークフローから何を受け取るのか。 ステートの入出力は 256 KiBまで UTF-8エンコードされた文字列として 上限解放不可 (ハードクォータ) Standard / Express 共通 ステート間の入出力 (ペイロード) のサイズをうまく調整してあげる必要がある。 15

Slide 16

Slide 16 text

Task ステートの入出力処理の順番 JSONPath と JSONata で入出力処理が変わる。JSONataの方がシンプルで分かりやすい。 https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/concepts-input-output-filtering.html 16

Slide 17

Slide 17 text

データフローシミュレータ コンソールのデータフローシミュレータは、 JSONPathの入出力処理5段階を1つずつ評価し て前後の JSON を並べてくれます。 現在は、非推奨 データフローシミュレータページは メンテナンスされなくなりました。… JSONATA クエリ言語をサポートしてい ません。 推奨は Workflow Studio の「テスト状態」/ TestState API。 17

Slide 18

Slide 18 text

JSONata なら、入出力処理が5つから2つになる 入力側 出力側 変数 JSONPath + Parameters ResultSelector + ResultPath + OutputPath Assign で保存、後続で参照可能 InputPath JSONata Arguments Output Assign で保存、後続で参照可能 JSONPathよりシンプルに処理できる。 QueryLanguage はステート単位で指定可能 既存はそのままJSONPathで、新しく書くステートからJSONataにすることが可能 Assign した変数は入出力ペイロードに載せずに渡せる 単一変数最大サイズ: 256 KiB / 1実行あたりの全変数合計: 10 MiB 別ステートマシンには変数は渡らない (スコープ外) 18

Slide 19

Slide 19 text

3. Mapステートで並列数を上げる

Slide 20

Slide 20 text

同じ子ワークフローを3,000個起動したいときは? Mapステートを使って子ワークフローを並列処理するのが王道 シンプルな処理では、Distributed Mapで並列実行数を簡単に引き上げることが可能 観点 同時実行数 実行履歴 入力 256KiB制限 Map 最大 40 程度 Mapステートを管理する親ワークフローに残る JSON配列のみ 受ける Distributed Map 最大10,000 「マップ実行」として独立して持つ JSON配列 / S3(CSV, JSON, JSONL, Parquet ほか) S3 から直接読む場合は回避可能 「S3バケット内の特定のパス配下にある全ファイルを処理する」ケースだと、Distributed Mapがシンプルかつ強力な武器となる。 20

Slide 21

Slide 21 text

Mapステートで複雑な並列処理の例 (1) 複雑な処理を並列で捌きたい場合は、Mapステートと入れ子構造で対応 例(1): イベント履歴上限回避のために、ステートマシンを4分割したケース https://speakerdeck.com/kasacchiful/jawsug-niigata-11?slide=28 21

Slide 22

Slide 22 text

Mapステートで複雑な並列処理の例 (2) 複雑な処理を並列で捌きたい場合は、Mapステートと入れ子構造で対応 例(2): 用途毎にステートマシンを分割したケース https://speakerdeck.com/kasacchiful/jawsug-akita-data-platform-with-container?slide=26 22

Slide 23

Slide 23 text

Distributed Mapでシンプルに並列処理数を上げる 23

Slide 24

Slide 24 text

子の起動速度は、ワークフローの種別で変わる Distributed Map の⼦ワークフロー起動速度(実測) 6秒 3,000件 30秒 2,500 2,000 凡例 1,500 実測 約550/秒(公称 up to 1,000 TPS) Express ⼦ Standard ⼦ 実測 ちょうど100/秒(公称 100 TPS) 1,000 500 0 0 5 10 15 20 到達点は同じ3,000。違うのは、そこまでの速さ 25 30秒 24

Slide 25

Slide 25 text

ワークフロー種別毎の起動速度比較 子の種別 公称 実測(3,000件) Express up to 1,000 TPS 6秒(約500件/秒) Standard 100 TPS 30秒(100件/秒) Standard は30秒間ぴたりと毎秒100件。 Express は公称「最大 1,000 TPS」に対して、実測値は約半分程度。 25

Slide 26

Slide 26 text

Distributed Mapの子ワークフローへの入出力 で各子に渡す入力を組み立てる JSONata が使える 大きなデータはペイロードに載せず、S3 のキーだけ渡して子ワークフロー側で読み込む ResultWriter で結果を S3 に書き出す 親ワークフローに集約しなくて済む あわせて設定しておきたいもの。 ItemSelector ItemBatcher 複数アイテムをまとめて1つの子に渡す。起動回数が減る ToleratedFailurePercentage エラー許容率。指定した割合までのエラーを許容してMap全体を成功として扱える。 既定だと 1件失敗した時点で他も中断される 26

Slide 27

Slide 27 text

実際に 3,000 並列実行してみた と設定し、1件あたり30秒の Lambda を3,000件。 Lambda内では30秒 wait しているだけ MaxConcurrency: 3000 27

Slide 28

Slide 28 text

実際に 3,000 並列実行してみた結果 確認事項 Step Functions 側の子実行 Lambda 最大同時実行数 Lambda スロットリング 全体の所要時間 結果 3,000件すべて起動完了 1,500(上限緩和して 1,500 にしている状態) 8,273回 265秒 所要時間の理想は 60秒くらい (3,000×30秒÷1,500) だが、実際には 約4.4倍時間がかかった 28

Slide 29

Slide 29 text

4. 並列数を上げた先で考慮すべきこと

Slide 30

Slide 30 text

壁は下流に移る 並列数を上げると、サービスクォータの壁は下流側のサービスに移っていく When using a distributed map, be sure to verify the quota on downstream services. — Distributed Map 発表ブログ https://aws.amazon.com/blogs/aws/step-functions-distributed-map-a-serverless-solution-for-large-scale-parallel-data-processing/ 30

Slide 31

Slide 31 text

並列実行数を上げた際に気になる Lambda の制限 壁 値 1 同時実行数 既定 1,000 / リージョン 2 スケール速度 1,000インスタンス / 10秒 / 関数 3 リクエスト速度 10,000 req/s 引き上げ 可 不可 不可 通常はあまり気にしない同時実行スケーリングレートだが、並列実行数を上げるとスロット リング発生につながる 公式ドキュメントの理解度テストの例がわかりやすい。 平均20ms の関数で 30,000 req/s を捌きたい。同時実行数は 30,000 × 0.02 = 600 で 足りそう。 しかし 10,000 req/s の上限に引っかかるので、同時実行数を3,000以上に上げる申請が 必要。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-concurrency.html 31

Slide 32

Slide 32 text

Step Functions 自身のクォータも確認しておく オープン状態の Map 実行の最大数: 1,000 Distributed Mapに適用 / ハードクォータ 超えた分は MapRunStarted イベントで待機される StartExecution API にもスロットリングがあり リージョンによって値が異なる 上限緩和可能 1.でイベント履歴 25,000 を回避するために入れ子ワークフローにしたが、その入れ子は StartExecution APIを呼んでいる。公式ドキュメントの通り、スロットリングに対応するた めに Retry を設定しておく方が良い。 Retry で StepFunctions.ExecutionLimitExceeded を使用して、実行が確実に開始さ れるようにします。 https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/concepts-nested-workflows.html 32

Slide 33

Slide 33 text

MaxConcurrency で制御する 同じ処理を MaxConcurrency: 1500 に絞って実行してみた。 3000 最大同時実行数 1,500 Throttles 8,273 所要時間 265秒 1500 1,500 0 128秒 最大同時実行数を絞ったら Throttles がゼロになり、しかも2倍以上速くなった! 上限を超えて投げても、下流が受け取れなければ Retry の往復が増えるだけ。 並列度は上げれば上げるほど良い、わけではないことに注意。 33

Slide 34

Slide 34 text

実測データ 左が MaxConcurrency: 3000 、右が 1500 。同じ処理、同じ件数。 34

Slide 35

Slide 35 text

まとめ Standard の実行履歴は 25,000イベントまで 履歴は「実行単位」 入れ子に分割すれば、各ワークフローで実行履歴 25,000イベントまで対応できる。 分割したら次はデータの受け渡し 必要な値だけをうまく入出力できるようにしておくこと。 Distributed Map は入れ子をシンプルに利用可能 子の起動速度は Express と Standard で5倍以上違う。 並列を上げるとサービスクォータの壁は下流に移る 特に各サービスのスケーリングや最大実行数に注意が必要。 上限緩和できるかどうかも確認しておく。 スロットリング対策として Retry 設定しておくと良い。冪等性の担保を忘れずに。 MaxConcurrency で制御する 最大実行数を絞ったほうが速いこともある 35

Slide 36

Slide 36 text

宣伝

Slide 37

Slide 37 text

今後のJAWS-UG新潟 2026/9/16 (水) JAWS-UG新潟#33 AWSサポートが言いたいセキュリ ティサービスあるあるPart2 AWS Expert Online for JAWS-UG #39 2026/10/03 (土) JAWS-UG新潟#34 AWS Step Functionsハンズオン 2026/12 頃 AWS re:Invent 2026 re:Cap (予定) 他、毎週木曜夜21時からオンラインで「プチキャッチア ップ会」も開催中! 詳細・参加申込は connpass ページをご確認ください。 37

Slide 38

Slide 38 text

おわり

Slide 39

Slide 39 text

参考リンク Step Functions service quotas(AWS Docs) Best practices — Starting new executions to avoid reaching the history quota Start workflow executions from a task state(入れ子ワークフロー) Transforming data with JSONata in Step Functions Map workflow state Understanding Lambda function scaling Step Functions Distributed Map(発表ブログ) 39