Cloud Functionsのパフォーマンス問題にどう立ち向かうのか
前書き
モバイルアプリのバックエンドにCloud Functions(for firebase)を使っていたときの話です。
諸々のデプロイ作業が終わって、アプリの動作確認していると、レスポンスが異常に遅いことが問題になりました。
コンソール上で各関数のパフォーマンスを調べてみたところ、1つの処理に4秒ほどかかっていたことが判明。この問題に立ち向かうべく、調査をしました。
Cold Start問題
Cloud Functionsは、1つのリクエストに対して、1つのインスタンスが作られ、そこで関数の処理が実行されます。インスタンスが作成されると、実行環境の初期化が行われ、その分の時間がかかってしまう。
1度インスタンスが作成されると、一定時間はそのインスタンスは維持され、再びその関数にリクエストがあった場合、インスタンスは再利用される。そのため、最初のリクエストよりも実行時間は少なくて済む。
ところが、関数がしばらく実行されないと、そのインスタンスは削除されてしまう。その後、再びリクエストがあったタイミングで、新しいインスタンスが作成される。新しいインスタンスが作成されると、実行環境はゼロから構築されるため、その分レイテンシが発生する→コールドスタート
Cloud Start問題への対応策
適切なリージョンに設定する
Cold Startとあまり関係ない気もするが、一応。
適切なリージョンに配置することで、レイテンシの改善が見込める。
今回は、日本で公開する予定のアプリであり、Cloud Functionsを含めた全てのリソースを、東京リージョンで設定しているため、問題にならないはず。
不要な依存モジュールが読み込まれないようにする
Cold Start時に、関数実行に使用しないモジュールまで読み込みされてしまうと、その分レイテンシが発生してしまう。そのため、Gincoさんの記事を参考にさせていただき、実行する関数のモジュールのみをロードするように修正。すごく勉強になりました。
min instancesを使う
最小インスタンスを設定してしまうと、その分料金が発生する。今回は予算の関係上、利用は厳しい。残念。
メモリの割り当て量を増やす
クックパッドさんの記事を参考にさせていただきました。これまた大変勉強になりました。
今回、Cloud Functionsの関数内で、Firestoreとのやりとりがあるが、Firestoreへの最初のリクエスト時には、新規にコネクションを貼る処理が発生する。Cloud Functionsの新しいインスタンスが立ち上がるたびに、Firestoreとの新規コネクション生成が発生するので、その分さらに時間がかかる。
色々と調査した結果、コネクションの確立を早くするためには、メモリの割り当てを増やすといいとのこと。当初、メモリの割り当てはデフォルトの256MBで、コールドスタート時の実行時間は4.5秒ほどかかっていた。メモリ使用量を見ても、それほど問題に思えなかったので、それに紐づいているCPUの割り当てが原因だと思う。メモリの割り当て量を512MBに増やしたところ、コールドスタート時の実行時間は2.2秒となった。さらに、1024MBに増やすと、1.3秒にまで改善した。
ただし、注意点として、メモリの割り当てを増やすと、それに応じて料金も上がるので、コストとパフォーマンスを天秤にかけて、よく考える必要がある。
参考文献
この記事は、以下の情報を参考にさせていただきました。大変勉強になりました。
Discussion