数千枚の写真台帳のExcel・PDF出力を支えるシステムを作った時の話

2025年9月26日

こんにちは!ミライ工事ウェブエンジニアの @t4traw です。

つい先日、ミライ工事の写真台帳出力を2000枚以上に対応し、非同期出力にする事で生成時間も他の操作をして時間を有効に使えるようになりました!

今回はその開発をした時の技術的な経験を記録したものになります。

ミライ工事ではRailsを中心にバックエンドを構築していて、一部サブモジュールをLambda+Goで動かしています。

写真台帳や点検表の出力もLambda(と、API GatewayやDynamoDBなど)だったのですが、数千枚の写真台帳を出力しようとすると、どうがんばってもLambdaの実行時間(15分)の限界に達する問題が発生していました。

これは、ミライ工事の自由なレイアウトでエクセルとPDFが作れるという独自性ゆえの問題で、この特徴をもったまま一つの台帳でもっとたくさんの写真を扱えるようにして欲しいユーザーの声に応えるため、様々なアプローチを検討していました。

検討したアプローチとStepFunctionsを選んだ理由

数千枚の写真台帳出力という課題に対し、複数のアプローチを検討しました。

  • 単純に実行時間を延ばせる環境(CloudRun)などに乗り換える?
    • 既存のLambda関数群を移行するコストが高く、また実行時間の制約を根本的に解決する保証がない
  • 処理を切り分けて、別々の実行環境でデータを作り、SQSなどで実行管理していく?
    • 処理の順序制御やエラーハンドリング、進捗管理を独自に実装する必要があり、開発工数が大きくなる課題
  • 実行サーバーを作ってしまう?
    • インフラの維持管理コストと、スケーラビリティの課題から除外

などなどです。そして、最終的にStepFunctionsを選ぶ事にしました。その理由は、

1. 既存アーキテクチャとの親和性

  • 現在のLambda関数を最小限の変更で活用可能
  • 新たなランタイム環境を用意する必要がない

2. 運用面のメリット

  • 処理の可視化とエラー追跡が標準で提供される
  • リトライやエラーハンドリングの設定が簡潔
  • 実行履歴の確認が容易

3. 開発工数の最小化

  • 分散処理の複雑な制御ロジックを書く必要がない
  • AWS管理のサービスによる高い可用性

4. スケーラビリティ

  • Mapステートによる並列実行で処理能力を柔軟に調整可能
  • 将来的な処理量増加にも対応しやすい

開発期間の短縮と運用の安定性を考慮すると、現段階では最適な選択だったと判断しました。

ペイロードの問題を解決する

LambdaとStepFunctionはInput/Outputで扱えるイベント(ペイロード)のサイズに制限があります。LambdaやAPIGatewayよりもStepFunctionsの制限の方が厳しく、2020年に増加されたものの、256KBになっています。

なので、まずはRails側でjsonをそのままbodyに入れてリクエストを投げるのではなく、S3にアップロードしたjsonのURLを渡すようにしました。

参考: AWS Step Functions はペイロードサイズを 256KB に増加

そして、それをAPIGatewayからStepFunctionsを起動するLambdaに渡し、StepFunctionsのInputとして設定します。

StepFunctionsの設計

まず、最終的に以下のような全体像になりました。

進行管理Step FunctionsStepFunctions起動用LambdaAPI GatewayS3Rails進行管理Step FunctionsStepFunctions起動用LambdaAPI GatewayS3Rails非同期処理開始Rails側で完成通知ユーザー台帳生成リクエストJSONデータをアップロードS3 URL返却APIリクエスト(S3のURL, AuthToken等)リクエスト受付Step Functions起動実行URL返却レスポンス(写真台帳の生成を開始しました)HTTPレスポンス処理開始通知Excel/PDFを生成し、S3に保存処理完了コールバック(完成ファイルのS3キー、状態等)台帳生成完了通知(ダウンロードリンク等)ユーザー

StepFunction内では一部重い処理をMapステートで分割するなどして処理をしています。

開発当初はあまりステップ数を増やさず、必要に応じて増やしていく予定でしたが、最終的には結構処理を分割していくことにしました。そちらの方がエラー管理やリトライなども柔軟にできるようになったためです。

余談ですが、この開発中にStepFunctionsで変数が扱えるようになったのですが、Input/Output周りや他の対応などを変えるのが大変で使えていません😭 イベントデータのバケツリレー状態は早めに改善したいですね……。

StepFunctionsのメリット・デメリット

StepFunctionという枠組みを使うことで、Mapステートによる並列実行、リトライやエラー処理の柔軟な設定、実行履歴の可視化などが簡単にできるようになりました。また、分岐もステップが可視化されて、非常に分かりやすくなりました。

一方で、開発の複雑さやコストは増加しました。GoはDockerに載せて動かしているのでECR、StepFunctions、DynamoDB、Lambda、S3をまとめてエミュレートするモノがなく、小さな関数はテストできるものの、結合テストはローカルで再現できないので、AWS上にdevelopment環境を作ってデプロイしてテストする必要があります。

コストも、StepFunctionのステップ数に応じて増えるので、コスト削減がシンプルにできません。具体的な金額を出せませんが、思ったより高いなぁと感じています。

これから

今は2000枚以上を目標に構築してありますが、ステップの処理をさらに分割するなどすれば、もっとたくさんの写真を扱えるようになると思います。

また、StepFunctionsの変数対応なども進んでいるので、そちらも活用していきたいですね。

そして開発の効率化をもっと進めて、そちらは別の記事で共有できれば良いなと思っています。

Tatsuro Moriyama

Tatsuro Moriyama

音楽と珈琲とサウナと釣りが好きです🤟 Ruby, Go, Typescriptでいろいろ書いてます😃 WEB開発、WEBデザイン、自動化などをよくやっています🚀