Google Cloud Functions のアンチパターンを回避する(第 2 回): Cloud Function インスタンスを再利用して将来の呼び出しに備える方法
Google Cloud Japan Team
※この投稿は米国時間 2021 年 10 月 28 日に、Google Cloud blog に投稿されたものの抄訳です。
編集者注: 今後数週間にわたり、Google Cloud Functions の関数を記述するためのベスト プラクティスに注目するブログ投稿シリーズを公開していきます。これらのベスト プラクティスは、サポートチームがよく受ける質問や、よく見かける誤解を基にしています。 こうした問題をここでは「アンチパターン」と呼び、これらを回避する方法をご紹介します。 この記事は、そのシリーズの第 2 回です。
シナリオ
Cloud Functions の関数が以下のいずれかの傾向を示していることに気づきます。
レスポンスが遅い
以降の実行時に予期せぬ動作をする
時間が経過するとメモリ不足になる
よくある根本的な問題
Cloud Functions の関数のレスポンスが遅い場合、一部のコードをグローバル スコープに移行することを検討すべきかもしれません。一方で、関数が 2 回目以降の実行時に予期せぬ動作をしたり、時間の経過とともにメモリ不足に陥ったりした場合、グローバル スコープに書かれたコードが問題の原因になることがあります。
調べる方法
関数のイベント ハンドラ ボディ内で、呼び出しのたびに時間やネットワークを消費するなど、負荷の高いオペレーションを実行していないでしょうか?例を以下に示します。
ネットワーク接続の開始
ライブラリ参照のインポート
API クライアント オブジェクトのインスタンス化
このような負荷の高いオペレーションは、グローバル スコープに移行することを検討すべきです。
グローバル スコープとは
グローバル スコープとは、関数のイベント ハンドラの外に書かれたコードのことです。グローバル スコープのコードは、インスタンスの起動時に一度だけ実行されます。将来の関数の呼び出しで、そのウォーム インスタンスが再利用されても、グローバル スコープのコードが再実行されることはありません。
技術的には、グローバル スコープのコードは、最初のデプロイ時に「ヘルスチェック」のために追加で実行されます。ヘルスチェックの詳細については、下記の「その他の役立つヒント」を参照してください。
グローバル スコープを使用するように関数を更新する方法
Firestore にデータを保存するとします。呼び出しのたびに接続を行うのではなく、グローバル スコープで接続を行うことができます。Cloud Functions は、前の関数の実行環境を可能な限り再利用しようとします。例えば、前のインスタンスがまだウォームの場合などです。つまり、変数をグローバル スコープで宣言することで、関数を高速化できる可能性があるということです。
注: これはあくまでも以前の環境が使われることを保証するものではありません。しかし、インスタンスが再利用できれば、パフォーマンスの向上が期待できます。
以下の例では、Firebase への接続が関数のイベント ハンドラのボディの外側にあることがわかります。関数のイベント ハンドラの外にあるものは、グローバル スコープになります。
遅延初期化とグローバル スコープ
グローバル スコープを使用する際には、遅延初期化に注意する必要があります。遅延初期化とは、実際に必要な場合にのみコードを初期化し、さらにはそのオブジェクトをグローバル スコープで永続化して再利用できるようにすることです。
例えば、関数で 2 つ以上のネットワーク接続を作成しなければならない可能性があるものの、どの接続が必要になるかは実行時までわからないとします。遅延初期化を使用すると、必要になるまで接続の作成を遅らせることができ、かつ次の呼び出しのためにその接続を保持できる可能性があります。
その他の役立つヒント
グローバル スコープでコードを書く際には、いくつかの注意点があります。
グローバル スコープで正しいエラー処理とロギングを行うことが最も重要です。コードが確定的なオペレーション(ライブラリの初期化など)を行い、グローバル スコープでクラッシュした場合、関数はデプロイに失敗します(「ヘルスチェック」と呼ばれていますが、詳細は後述します)。しかし、グローバル スコープで非確定的なオペレーションを行っている場合(例えば、どのような関数の呼び出しでも断続的に失敗する可能性のある API を呼び出している場合)、ログに「Could not load the function, shutting down」や「Function failed on loading user code.」といったエラーが表示されます。失敗時に自動的に再試行する機能を有効にしているバックグラウンド関数では、ユーザーコードのエラーによる失敗を Pub/Sub が再試行します。最も重要なポイントは、グローバル スコープのコードをテストし、適切なエラー処理とログを記述したことです。グローバル スコープでコードを実行する際に関数のデプロイが失敗することについては、トラブルシューティング ガイドで詳しく説明しています。
例えば、「console.log()」ステートメントの出力など、グローバル スコープのコードから得られる情報がログに追加されていることに驚くかもしれません。Cloud Functions の関数がデプロイされると、ヘルスチェックが行われ、ビルドが成功し、関数に適切なサービス アカウントのアクセス許可があることが確認されます。このヘルスチェックでは、グローバル スコープで書かれたコードが実行されるため、ログには「余分な」呼び出しが含まれています。
- Cloud デベロッパー アドボケイト Sara Ford
- テクニカル ソリューション エンジニア Martin Skoviera