こんにちは。エンジニアの森本です。
kickflowでは、申請するチケットに対してファイルを添付することができます。これらをActive Storage のダイレクトアップロードを使って実現しています。 以前からファイルを大量に添付したチケットを申請すると、タイムアウトが発生してしまう問題がありました。APMサービスを見てみると、ファイルをダウンロードしている処理がありました。 今回は、ダイレクトアップロードを利用していてファイルを大量にアップロードしたときにタイムアウトが発生しないように行った工夫を共有したいと思います。
ダイレクトアップロードとは
ダイレクトアップロードとは名前の通り、S3やGoogle Cloud Storageなどのクラウドストレージにブラウザから直接アップロードする機能です。サーバに直接ファイルをアップロードしないため、サーバへの負荷や転送量を抑えられる手法で、Herokuでも推奨されています。
Active Storage のダイレクトアップロードを利用するとサーバにファイルをアップロードせずattachを行えると思いませんか? 実は、サーバ側でファイルのダウンロードを行っているんです。
なぜファイルをダウンロードしているのか
active_storage_blobs
テーブルの metadata
カラムはJSONで保存されていますが、その中にidentified
という要素があることをご存知でしょうか?
identified
は、ファイルのContent-Typeを判定する処理の完了を示すフラグのようです。
具体的には、 以下で行われています。
この処理で、Content-Typeの判定のために、4KB分だけファイルをダウンロードしています。
この処理はファイルをattachした際に自動的に呼び出されます。通常の利用では4KBのファイルダウンロードでは問題になりにくいです。
しかしkickflowでは、以下の2点により問題が顕在化しました。
- 1回の申請で100件までの添付ファイルをアップロード可能
- HerokuはUSリージョン、S3は東京リージョンにありファイルダウンロードに時間がかかる
1ファイルのダウンロードに200ミリ秒前後かかるため、ファイルダウンロードだけで20秒程度かかってしまいます。
解決方法
弊社では、ダイレクトアップロードが完了したファイルから先行してContent-Typeの判別処理を走らせることにしました。
具体的には、ActiveStorage::Blob::Identifiable#identify
を呼び出すことでContent-Typeの判定を行うことができます。
これにより、attach時に、ファイルダウンロードが行われず処理時間を短縮することができました。
blob = ActiveStorage::Blob.find_signed!(params[:signed_id]) blob.identify unless blob.identified?
添付ファイルをダイレクトアップロードにしているのに、ファイルダウンロードが発生、タイムアウトしてしまい困っている方の参考になれば幸いです。
We are hiring!
kickflow(キックフロー)は、運用・メンテナンスの課題を解決する「圧倒的に使いやすい」クラウドワークフローです。
サービスを開発・運用する仲間を募集しています。株式会社kickflowはソフトウェアエンジニアリングの力で社会の課題をどんどん解決していく会社です。こうした仕事に楽しさとやりがいを感じるという方は、カジュアル面談、ご応募お待ちしています!