0. はじめに
こんにちは、SWE のやなせです。 暑い日が続きますね。この記事を書いている日の最高気温は34℃だそうです。34℃という数字を見てもすっかり慣れてしまった自分に驚きました。
今回は自分が所属する施工案件管理チームで一番ホットな、2023年7月にリリースした「写真台帳 Excel 出力機能」の概要と、その実装時に直面した技術的課題について紹介します。
1. 施工案件管理と写真台帳作成について
私の所属する施工案件管理チームでは、ANDPAD のコア機能である施工案件管理を開発・保守しています。
工事現場では、施工前から完成後まで多くの写真を撮影し、工事が適切に施工されたことを証明するために、その写真を写真台帳で管理・提出する必要があります。
ANDPAD の案件管理機能には「写真台帳出力機能」があり、ANDPAD に保存した写真をもとに、この写真台帳を Web 上で作成・編集・出力する機能を提供しています。
2. 新機能の背景
従来の写真台帳出力機能では、作成した台帳の「印刷」「PDF 出力機能」のみを提供していました。しかし利用者が増えるなかで、ユーザーの皆様から写真台帳出力機能に対し「台帳の表紙をカスタマイズしたい」「中表紙を入れたい」といった、大小多くの要望を頂くようになりました。
これらの要望に対し、ひとつひとつの機能を実装することも考えられました。しかし「多くのユーザーにいち早く価値を届けるにはどのようにすればよいか」を検討した結果、ユーザー自身が台帳を編集可能な Excel 形式で出力する機能を開発する運びとなりました。
3. Excel出力実装におけるボトルネック
Excel 出力機能の実装にあたって、 ANDPAD (Rails アプリケーション) から利用できそうな社内リソースに Amazon API Gateway (REST API) と AWS Lambda で構築されたマイクロサービスがありました。これは雛形となる Excel ファイルと写真の S3 のパスをリクエストパラメータとして渡すことで、写真埋め込み済の Excel ファイルのパスをレスポンスとして返す API を提供しています。当初はこのマイクロサービスを改修なく利用できると考えていました。
しかし、今回「台帳に写真は3,000枚まで添付できること」という性能要件があがり、これを実現するには同期処理しか対応していない従来のマイクロサービスだと、Amazon API Gateway 側のタイムアウトの制約(1リクエストにつき最大29秒)*1に引っかかってしまうことが判明しました。
4. ボトルネックを解消するための Lambda 非同期呼び出し
今回はこの同期処理時のタイムアウトを解消するために、マイクロサービスを非同期処理化する改修を行いました。
調査を進めていくなかで、Amazon API Gateway の REST API から非同期に Lambda を呼び出し、ジョブキュー的にイベントを処理する設定があるとわかり、今回はこちらを採用しました。AWS Lambda はデフォルトだと同期的に処理を実行しますが、タイプとして Event
を指定することで、Lambda 関数を非同期で呼び出されるように設定できます。*2
そして、AWS Lambda の同期処理を利用している機能と共存するために今回は新たに非同期処理用のエンドポイントを作成し、Rails からはそのエンドポイントを叩くように実装しました。
また、非同期的に呼び出した処理が完了した際の通知先として、Rails 側に成功・失敗時の通知用エンドポイントをそれぞれ立てました。Rails からのリクエスト時に、通知先 URL (callbackUrl )を渡すようにし、正常終了した際には通知先 URL へ、写真埋め込み済の Excel ファイルがアップロードされた S3 バケット名とキーをリクエストボディとしてリクエストを送るようにしました。
非同期化に伴う一連の流れを、図にすると下記のようになります。
また、図には載せていませんが、Lambda の処理がキューイングされたのちに Lambda 内外の処理で失敗した場合はデッドレターキュー(Amazon SQS) にキューイングし、失敗通知用の Lambda で Rails への失敗時用のエンドポイントを叩き、ユーザーに処理が失敗したことを通知するようにしています。
5. さらなるボトルネックと解決
非同期処理化し、これで解決…!かと思いきや、またしても「台帳に写真は3,000枚まで添付できること」という要件がネックになりました。
3,000枚出力の検証時に413エラー(413 Request Entity Too Large)を返すリクエストが見つかり、調査の結果、ペイロードの上限が原因であることがわかりました。
今回、リクエスト時に S3 のパスをリクエストパラメータとして渡す関係上、添付する写真枚数が多いとリクエストパラメータの文字列長が長くなってしまいます。この際、マイクロサービスに向けてリクエストした際に AWS API Gateway が許容するペイロードの上限を超過してしまい、413エラーを返していることがわかりました。*3
この値は上限を引き上げることが出来ないようでしたので、今回は回避策としてリクエストパラメータとして送っていた写真の S3 パスや写真埋め込みのための設定パラメータ群を、リクエスト前に S3 へ JSON ファイルとしてアップロードし、そのパスをリクエスト時に送るようにすることでこの問題を解消しました。
そして、最終的には下記のようになりました。
容量の大きな画像や動画を POST で送信しようとする際、ペイロードサイズの上限に達する問題が生じることがあります。この問題を回避するためのよくある手法として、別途ファイルをアップロードし、そのパスを渡す方法がありますが、このアプローチをサーバーレスアーキテクチャでも同様に活用することができました。
これらの非同期化に伴う対応により、性能要件である「写真3,000枚の出力」、API Gateway 側のタイムアウト制約である「29秒」をクリアすることができ、なんとかリリースに漕ぎ着けることが出来ました。やっぱり公式をちゃんと読むって大事ですね。
6. まとめ
今回はアンドパッドが新たにリリースした「写真台帳 Excel 出力機能」の開発背景、技術的な課題と解決方法について説明しました。特に、AWS API Gateway 側のタイムアウト制約を解消する方法として非同期的に AWS Lambda の処理を実行し、同時に AWS API Gateway 側のペイロード上限を回避するために写真の S3 パスなどの設定パラメータ群を事前に S3 にアップロードし、AWS Lambda 側で取得する方法について説明しました。
今回、非同期化を対応するにあたって当初考えていた開発期間よりも長くかかってしまったのですが、リリース後に利用状況を計測した結果、同期処理のままだとタイムアウトする枚数が実際に出力されていたのを観測し、非同期化を採用した価値があったと感じられました。
7. おわりに
アンドパッドでは、「幸せを築く人を、幸せに。」というミッションの実現のため、一緒に働く仲間を大募集しています。 数あるユーザーの課題を解決するには何が最善かを考え抜き、よい機能・よいプロダクトを作りたい!と思われる方はぜひぜひご応募ください!