Step FunctionsのState出力、256KB超えると普通に落ちる

  • AWS
  • Step Functions
  • Fargate

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

Parallel StateでFargateタスクをいくつか並列に回すバッチ処理を組んでたら、こんなエラーで落ちた。

The state/task 'example-task' returned a result with a size exceeding the maximum number of bytes service limit.

何が起きてたか

Step Functionsには、1つのState(Task/Parallel/Mapなど)が扱える入力・出力のサイズに上限がある。**256 KiB(262,144バイト)**で、UTF-8文字列としてカウントされるハードクォータ。超えるとStates.DataLimitExceeded系のエラーで実行が落ちる。

ちなみにこの上限、昔は32,768バイト(32KB)だった。2020年9月のアップデートで256KiBまで緩和されてる。今回引っかかったのはこっちの256KiBの方。

原因は主に2つ重なってた。

  1. ECS RunTaskのレスポンスをそのままStateの出力に載せてた。Attachments・NetworkInterfaces・Tagsとかまるごと入ってくるので、1タスク分でもそこそこの量になる
  2. Parallel Stateは各branchの出力を配列にまとめて親に返す。branchが増えるほど単純に合算されて、すぐ256KiBに刺さる

直した内容

Stateに載る出力そのものを削る方向で対処した。

  • 各branchのResultSelectorで、ECS RunTaskの結果からTaskArnとタスクの終了コードくらいしか残さないようにした
  • ログ・デバッグ用途で全量が欲しい部分はResultPath: nullにしてStateの出力自体には反映させず、CloudWatch Logs側で確認する運用にした

流れで書くとこんな感じ。

graph LR
  A[Parallel State] --> B[Branch 1: Fargate Task]
  A --> C[Branch 2: Fargate Task]
  A --> D[Branch N: Fargate Task]
  B --> E[ResultSelector で TaskArn だけ抽出]
  C --> E
  D --> E
  E --> F[Parallel の出力へマージ]
  B -.実行ログ.-> G[CloudWatch Logs]
  C -.実行ログ.-> G
  D -.実行ログ.-> G

今回はこの2つで256KiBの範囲に収まったので、S3への退避はやってない。もしこれでも収まらないくらいデータ量が増えるようなら、実データをS3に書き出してStateにはパスだけ返す構成を検討することになりそう。

分かったこと

  • Step Functionsを「データ運搬」に使おうとすると割とすぐ壁に当たる。まずはStateに載せる出力自体を削る(ResultSelector・ResultPath: null)で対応できるケースは多そう
  • Parallelは各branchの出力が合算されることを忘れがちなので、branch数が増える設計をするときは特に注意した方がいい
  • ResultPath・OutputPath・ResultSelectorのどれを使うかで挙動がだいぶ変わるので、Stateごとにどれを使ってるか把握しておかないと原因追跡がしんどくなる
  • それでも出力が大きすぎる場合は、S3に実データを退避してStateには参照だけ持たせる構成が定石らしい(今回は未実施)