重試非同步函式

本文說明如何要求非同步 (非 HTTPS) 背景函式在失敗時重試。

重試的語意

Cloud Functions 保證針對事件來源產生的每個事件,至少執行一次事件導向函式。根據預設,如果函式叫用因錯誤而終止,系統不會再次叫用該函式,並會捨棄事件。啟用事件導向函式的重試功能後,Cloud Functions 會重試失敗的函式叫用,直到成功完成或重試期結束。

針對第 2 代函式,這個重試期會在 24 小時後到期。第 1 代函式會在 7 天後到期Cloud Functions 會使用指數輪詢策略,重試新建立的事件驅動函式,同時增加 10 至 600 秒之間的輪詢。這項政策會在您初次部署新函式時套用至新函式。即使重新部署函式,即使重新部署函式之前,首次部署的函式早於本版本資訊中所述變更前,這類函式也不會回溯套用至這些函式。

根據預設,如果函式並未啟用重試功能 (預設值),函式一律會回報執行成功,且記錄檔中可能會顯示 200 OK 回應代碼。即使函式發生錯誤也是如此。如要明確表示函式發生錯誤時,請務必適當回報錯誤

事件導向函式為何無法完成

在極少數情況下,函式可能會因內部錯誤而提早結束,根據預設,函式可能會也可能不會自動重試。

更常見的情況是,由於函式程式碼本身擲回錯誤,事件導向函式可能無法順利完成。可能原因如下:

  • 函式包含錯誤且執行階段擲回例外狀況。
  • 函式無法連至服務端點,或在嘗試這麼做時逾時。
  • 函式刻意擲回例外狀況 (例如,參數驗證失敗時)。
  • Node.js 函式會傳回遭拒的承諾,或將非 null 值傳遞至回呼。

在上述任何一種情況下,函式都會根據預設停止執行,並捨棄事件。如要在發生錯誤時重試函式,您可以透過設定「失敗時重試」屬性,變更預設重試政策。這會導致系統重複重試事件,直到函式順利完成或重試逾時到期為止。

啟用或停用重試

透過 GCP 控制台設定重試作業

如果您要建立新的函式:

  1. 在「建立函式」畫面中選取「新增觸發條件」,然後選擇要做為函式觸發條件的事件類型。
  2. 在「Eventarc trigger」窗格中,選取「Restart on failed」核取方塊來重試。

如要更新現有函式:

  1. 在「Cloud Functions 總覽」頁面中,按一下要更新的函式名稱開啟「函式詳細資料」畫面,然後從選單列中選擇「編輯」,以顯示「HTTPS」和「Eventarc」觸發窗格。
  2. 在「Eventarc trigger」窗格中,按一下 edit 圖示,即可編輯觸發條件設定。
  3. 在「Eventarc trigger」窗格中,選取或取消勾選「Restart on failed」核取方塊,即可啟用或停用重試作業。

透過函式程式碼設定重試作業

透過 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;
}

catch 與 Promise 搭配使用

如果您的函式已啟用重試功能,任何未處理的錯誤都會觸發重試。並確認程式碼會擷取不應導致重試的所有錯誤。

以下是正確做法的範例:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

將可重試的事件導向函式設為冪等

可重試的事件驅動函式必須為冪等。以下是將這類函式設為冪等的一些一般準則:

  • 許多外部 API (例如 Stripe) 都可讓您提供冪等鍵做為參數。如果您使用這類 API,應使用事件 ID 做為冪等鍵。
  • 冪等性適用於至少傳送一次的傳送作業,因為這樣可以安全重試。因此,編寫可靠程式碼的一般最佳做法是將冪等與重試結合使用。
  • 請確保您的程式碼為內部冪等。例如:
    • 確保異動可以發生多次,且不會變更結果。
    • 在變更狀態之前,查詢交易中的資料庫狀態。
    • 確保所有連帶影響本身也是冪等的。
  • 在函式外強制執行交易檢查,與程式碼無關。 例如,將已處理的特定事件 ID 保留在記錄位置的狀態。
  • 請在頻外處理重複的函式呼叫。例如,在重複的函式呼叫後,使用另外的清除程序清除所用資源。

設定重試政策

視 Cloud 函式的需求而定,您可能會想要直接設定重試政策。這樣您就可以設定以下的任何組合:

  • 請將重試期從 7 天縮短至 10 分鐘。
  • 變更指數輪詢策略的最短和最長輪詢時間。
  • 請變更重試策略以立即重試。
  • 設定無效信件主題
  • 設定傳送嘗試次數上限和下限。

設定重試政策的步驟如下:

  1. 編寫 HTTP 函式。
  2. 使用 Pub/Sub API 建立 Pub/Sub 訂閱項目,並將函式網址指定為目標。

如要進一步瞭解如何直接設定 Pub/Sub,請參閱 Pub/Sub 說明文件中的處理失敗問題