重試非同步函式

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

重試的語意

Cloud Functions 會針對事件來源發出的每個事件,執行至少一次事件驅動函式。根據預設,如果函式叫用作業因錯誤而終止,系統就不會再次叫用該函式,並且會捨棄事件。在事件驅動函式上啟用重試功能時,Cloud Functions 會重試失敗的函式叫用,直到成功完成或重試時間窗口到期為止。

對於第 2 代函式,這個重試時間窗會在 24 小時後到期。對於第 1 代函式,則會在 7 天後失效。Cloud Functions 會使用指數輪詢策略重試新建立的事件驅動函式,其中輪詢時間會逐漸增加,介於 10 到 600 秒之間。這項政策會在您首次部署新函式時套用。即使您重新部署函式,這項變更也不會溯及先前部署的現有函式,也就是在這份版本資訊中所述變更生效之前部署的函式。

如果未為函式啟用重試功能 (這是預設值),函式一律會回報已成功執行,且記錄檔中可能會顯示 200 OK 回應碼。即使函式發生錯誤,也會發生這種情況。為清楚指出函式遇到錯誤時的情況,請務必適當地回報錯誤

事件導向函式無法完成的原因

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

更常見的是,事件驅動函式可能會因函式程式碼本身擲回的錯誤而無法順利完成。發生這種情況的原因包括:

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

在上述任何情況下,函式都會依預設停止執行,且事件會遭到捨棄。如要在發生錯誤時重試函式,您可以設定「retry on failure」屬性來變更預設重試政策。這會導致事件重複重試,直到函式順利完成或重試逾時為止。

啟用或停用重試

透過控制台設定重試

如果您要建立新函式:

  1. 在「Create Function」畫面中,點選「Trigger」下方的下拉式選單,然後選擇要用於觸發函式的事件類型。
  2. 勾選「在失敗時重試」核取方塊,啟用重試功能。

如果要更新現有函式:

  1. 在「Cloud Functions 總覽」頁面中,按一下要更新的函式名稱,開啟該函式的「函式詳細資料」畫面,然後在選單列中選擇「編輯」,即可顯示「觸發條件」窗格。
  2. 選取或清除「失敗時重試」核取方塊,即可啟用或停用重試功能。

從函式程式碼設定重試

有了 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 已處理。
  • 請在頻外處理重複的函式呼叫。例如,您可以使用單獨的清理程序,在重複的函式呼叫後進行清理。

設定重試政策

視函式的需要而定,您可能需要直接設定重試政策。這樣一來,您就可以設定下列任意組合:

  • 將重試時間範圍從 7 天縮短至最短 10 分鐘。
  • 變更指數回退重試策略的最小和最大回退時間。
  • 變更重試策略,以便立即重試。
  • 設定無效信件主題
  • 設定傳送嘗試次數上限和下限。

如要設定重試政策,請按照下列步驟操作:

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

如要進一步瞭解如何直接設定 Pub/Sub,請參閱 Pub/Sub 說明文件