Firebase Functions で動画や音声の変換をしようとして詰まったこと
経緯
現在展開しているサービス「推しボ!」は、ユーザが一言程度のセリフを投稿し、またユーザがそのセリフを音声化して投稿する、「声」に着目したSNSを目指すサービスです。また他にも、現在起業準備中の高齢者向けサービスでも、音声や動画のやり取りがありその変換などが課題になっています。
基本的にどちらもPWAを想定したWEBアプリケーションとなっており、ブラウザで録音・録画、ストレージはFirebaseのStorageで、動画が投稿されるとFirebaseのFunctionsが動いて変換を行う仕組みにしています。
ブラウザでの録音・録画はこれまた結構曲者で大変なのですが、そちらは別件になるので次の記事にしようと思います。
で、始まりはFirebase Extensionsにある「Resize Images」という拡張でした。これは投稿された画像ファイルを好きなサイズ、フォーマットに変換してくれる非常にありがたい拡張で、iOSがWebPに対応してからは全部こちらを使用して画像の変換をしていましたが、リージョン関連の不具合と思うのですが、いつからか上手く動作しなくなり、自前でFunctionsを作るようになりました。この時点では特に問題はありませんでした。
そのうち、音声や映像の需要も出てきて、同じようにFunctionsで変換させるように作成したのですが、ローカルでは全く問題ないものの、実環境では度々失敗することがありました。ただそれも、同じファイルでも成功することも失敗することもあり、正直原因が良くわかりませんでした。
結論
現在のところFunctionsのタイムアウトが原因だったと結論付けています。
調べたところFunctionsの初期設定では60秒でタイムアウトになっているようです。今回は余裕をもって360秒に設定したところ、失敗は起こらなくなりました。それどころか、これまで1分ほどかかっていた変換処理が超爆速になりました。
exports.convertFiles = onObjectFinalized({
timeoutSeconds: 360,
bucket: BUCKET_NAME
}, async (event) => {
//...
})
参考:https://firebase.google.com/docs/functions/manage-functions?hl=ja&gen=2nd
構成
画像の変換には「Firebase Extensions」の「Resize Images」と同じ「sharp」、動画、音声の変換には定番中の定番「FFmpeg」を使用しています。他の選択肢はあまりないんじゃないでしょうか。
兎に角Functionsでは「FFmpeg」はちゃんと動作します。VercelのAPIで動かすのはほとんど無理だと思いますし、AWSのLambdaで動かすのは結構大変だと思いますが、そこの所は非常にありがたいですね。
実際のコードについてはここでは秘密にしておきます。
Storage駆動のFunctionsは下手に実装すると無限変換編が始まって無限のファイルを作成し始めます。そうなるとちょっと責任負えませんので。
気になる方はコメントで聞いてください。
タイムアウトの仕様(予想)
で、何故今回このようなことが発生したか、ですが、初期設定1分があまりに短い事と、その辺りの仕様が不明瞭なことに原因があると思います。
確かに、DBと接続するような基本的なAPI、処理においては、1分でタイムアウトというのは十分な時間であり、問題ないと思われます。
ところが、Functionsのような明らかにファイルを扱うようなケースにおいては、1分は十分ではありません。それも、これも恐らくの予想の域ですが、タイムアウトまでの時間にインスタンスの起動時間、所謂コールドスタートの時間も含まれているように思います。
如何に動画の変換といっても、高々1~2分の動画で、しかも実際は音声しかありません。そのようなファイルの変換に1分かかるのはさすがにおかしいと思います。実際、タイムアウトまでの時間を延ばした後、変換自体はほんの数秒で完了していることを確認しています。変換というよりは、FFmpegという結構大きなライブラリ(?)を動かすための準備に1分だとかそういう時間がかかっているのだと思います。
ここではFunctionsのタイムアウトに関する挙動を完全に予想で並べようと思います。ほとんど予想ですので、細かい仕様をご存じの方がいらっしゃったら訂正をお願いします。
・初期設定でのタイムアウトは60秒である(公式)
・タイムアウトの最長設定は9分である(公式)
・タイムアウト後の起動はコールドスタートで待機時間がある(公式)
・コールドスタートの間の時間(起動時間)もタイムアウトの対象に含まれる(予想)
・特に設定しない限りタイムアウト時にタイムアウトエラーが出力されたりはしない(予想)
・FFmpegを使える状態にするために1分弱の時間を要する(予想)
・動画の変換自体は数秒~数十秒で完了する(予想)
・処理が終わってもタイムアウト時間を迎えるまではインスタンスは維持され、新しい接続があれば使いまわされる(予想)
・新しい接続があった場合はタイムアウト時間はリセットされ延長される(適当予想)
最終結論
重たい処理をさせる場合、ちょうど10分ごとに処理があるとか、めちゃくちゃコストを気にする、とかでない限り、タイムアウトは9分に設定しておいてよいと思う。
コスト以外に長くするデメリットがほとんどない。ただし、ローカルストレージは破棄されるまで維持されると思うので、毎回きちんと綺麗にすること。
Discussion