このドキュメントでは、失敗時に再試行する非同期 (非 HTTPS) バックグラウンド関数を要求する方法について説明します。
再試行のセマンティクス
Cloud Functions は、イベント ソースによって発行されたイベントごとに、イベント ドリブン関数の少なくとも 1 回の実行を保証します。ただし、デフォルトでは、関数呼び出しがエラーで終了した場合、関数は再度呼び出されず、イベントはドロップされます。イベント ドリブン関数で再試行を有効にすると、Cloud Functions は、正常に完了するか、再試行ウィンドウが期限切れになる (デフォルトでは 7 日後) まで、失敗した関数呼び出しを再試行します。
関数の再試行が有効になっていない場合 (デフォルト)、関数は常に正常に実行されたことを報告し、ログに200 OK
応答コードが表示されることがあります。これは、関数でエラーが発生した場合でも発生します。関数でエラーが発生したときにそれを明確にするために、エラーを適切に報告してください。
イベント ドリブン関数が完了しない理由
まれに、内部エラーが原因で関数が途中で終了することがあります。デフォルトでは、関数は自動的に再試行される場合とされない場合があります。
より一般的には、関数コード自体でスローされたエラーが原因で、イベント ドリブン関数が正常に完了しない場合があります。これが発生する理由には、次のようなものがあります。
- 関数にバグが含まれており、ランタイムが例外をスローします。
- 関数がサービス エンドポイントに到達できないか、エンドポイントに到達しようとしている間にタイムアウトになりました。
- 関数は意図的に例外をスローします (たとえば、パラメーターが検証に失敗した場合)。
- Node.js で記述された関数が拒否された promise を返すか、
null
の値をコールバックに渡す場合。
上記のいずれの場合でも、関数はデフォルトで実行を停止し、イベントは破棄されます。エラーが発生したときに関数を再試行する場合は、「失敗時に再試行する」プロパティを設定して、デフォルトの再試行ポリシーを変更できます。これにより、関数が正常に完了するまで、最大数日間、イベントが繰り返し再試行されます。
再試行の有効化と無効化
GCP コンソールの使用
次のように、GCP Console で再試行を有効または無効にできます。
Cloud Platform Console のCloud Functions の概要ページに移動します。
[関数を作成] をクリックします。または、既存の関数をクリックしてその詳細ページに移動し、[編集] をクリックします。
関数の必須フィールドに入力します。
Triggerフィールドが、Cloud Pub/Sub や Cloud Storage などのイベントベースのトリガー タイプに設定されていることを確認します。
詳細 をクリックして詳細設定を展開します。
Retry on failureというラベルの付いたボックスをオンまたはオフにします。
関数コード内
Cloud Functions for Firebase を使用すると、関数のコードで再試行を有効にできます。 functions.foo.onBar(myHandler);
、 runWith
を使用して障害ポリシーを構成します。
functions.runWith({failurePolicy: true}).foo.onBar(myHandler);
示されているようにtrue
を設定すると、関数が失敗時に再試行するように構成されます。
ベストプラクティス
このセクションでは、再試行を使用するためのベスト プラクティスについて説明します。
再試行を使用して一時的なエラーを処理する
関数は実行が成功するまで継続的に再試行されるため、再試行を有効にする前に、テストを通じてバグなどの永続的なエラーをコードから排除する必要があります。再試行は、不安定なサービス エンドポイントやタイムアウトなど、再試行時に解決される可能性が高い断続的/一時的な障害を処理するために最適に使用されます。
無限の再試行ループを回避するための終了条件を設定する
再試行を使用する場合は、連続ループから関数を保護することをお勧めします。これを行うには、関数が処理を開始する前に、明確に定義された終了条件を含めます。この手法は、関数が正常に開始され、終了条件を評価できる場合にのみ機能することに注意してください。
シンプルでありながら効果的なアプローチは、特定の時間よりも古いタイムスタンプを持つイベントを破棄することです。これにより、障害が永続的であるか、予想よりも長く続く場合に、過剰な実行を回避できます。
たとえば、次のコード スニペットは、10 秒より古いすべてのイベントを破棄します。
const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
callback();
return;
}
Promise でcatch
を使用する
関数で再試行が有効になっている場合、未処理のエラーによって再試行がトリガーされます。コードが、再試行に至らないエラーをすべてキャプチャしていることを確認してください。
以下は、何をすべきかの例です。
return doFooAsync().catch((err) => {
if (isFatal(err)) {
console.error(`Fatal error ${err}`);
}
return Promise.reject(err);
});
再試行可能なイベント ドリブン関数をべき等にする
再試行できるイベント ドリブン関数はべき等でなければなりません。このような関数を冪等にするための一般的なガイドラインを次に示します。
- 多くの外部 API (Stripe など) では、べき等キーをパラメーターとして指定できます。このような API を使用している場合は、イベント ID をべき等キーとして使用する必要があります。
- 冪等性は、安全に再試行できるため、少なくとも 1 回の配信でうまく機能します。したがって、信頼性の高いコードを作成するための一般的なベスト プラクティスは、冪等性と再試行を組み合わせることです。
- コードが内部的にべき等であることを確認してください。例えば:
- 結果を変更することなく、突然変異が複数回発生する可能性があることを確認してください。
- 状態を変更する前に、トランザクションでデータベースの状態をクエリします。
- すべての副作用自体がべき等であることを確認してください。
- コードとは関係なく、関数の外部でトランザクション チェックを課します。たとえば、特定のイベント ID が既に処理されたことを記録する状態をどこかに永続化します。
- 重複する関数呼び出しを帯域外で処理します。たとえば、関数呼び出しが重複した後にクリーンアップする別のクリーンアップ プロセスを用意します。