共用方式為


Durable Functions(Azure Functions)中的編排版本控制 - 公開預覽

協調流程版本控制可解決將變更部署至協調器函式 的核心挑戰 ,同時維護 Durable Functions 所需的決定性執行模型。 如果沒有這項功能,協調器邏輯或活動函式簽章的重大變更會導致重新執行期間執行中協調流程執行個體發生失敗,因為它們會中斷確保可靠協調流程執行的決定性需求。 此內建功能提供最低設定的自動版本隔離。 它不涉及後端,因此利用任何 Durable Functions 儲存體提供者的應用程式可以使用它,包括長期工作排程器

Note

對於 Durable Task Scheduler 使用者,如果您使用 Durable Task SDK 而非 Durable 函數,您應該參閱 Durable Task SDK 版本文章

Terminology

本文使用兩個相關但不同的詞彙:

  • Orchestrator 函式 (或只是「協調器」):是指定義工作流程邏輯的函式程式碼 - 工作流程執行方式的範本或藍圖。
  • 協調流程實例(簡稱「協調流程」):指的是協調器功能的具體執行,其自身具有狀態、實例 ID 和輸入。 多個協調流程實例可以從相同的協調器函式同時執行。

瞭解這項區別對於協調流程版本控制至關重要,其中協調器函式程式代碼包含版本感知邏輯,而協調流程實例在建立時會永久與特定版本相關聯。

運作方式

協同版本控制功能運作依以下核心原則:

  • 版本關聯:建立編排實例時,它會永久與一個版本相關聯。

  • 版本感知執行:Orchestrator 函式程式代碼可以據以檢查與目前協調流程實例和分支執行相關聯的版本值。

  • 回溯兼容性:運行較新版指揮者版本的工人可以繼續執行由舊版指揮者創建的協作實例。

  • 前向保護:執行階段會自動防止執行較舊的協調器版本的工作執行者運行由較新的協調器版本啟動的協調流程。

Important

協調流程版本設定目前為公開預覽狀態。

先決條件

在使用協調流程版本設定之前,請確定您擁有程式設計語言所需的套件版本。

如果您使用 non-.NET 語言 (JavaScript、Python、PowerShell 或 Java) 搭配 延伸套件組合,您的函式應用程式必須參考 延伸套件組合 4.26.0 版或更新版本。 設定 extensionBundle 中的 host.json 範圍,以確保最低版本至少為 4.26.0,例如:

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[4.26.0, 5.0.0)"
    }
}

請參閱擴充 套件組合組態文件, 以取得選擇及更新套件組合版本的詳細資訊。

使用Microsoft.Azure.Functions.Worker.Extensions.DurableTask1.5.0版或更新版本。

基本用法

協調流程版本控制最常見的使用案例是,當您需要對協調器邏輯進行重大變更,同時讓現有的實時協調流程實例以原始版本執行時。 您只需要更新 defaultVersion 中的 host.json,並修改協調器程式程式碼,據以檢查協調流程版本和分支執行。 讓我們逐步解說所需的步驟。

Note

本節中所述的行為會以最常見的情況為目標,而這就是預設組態所提供的行為。 不過,如有需要,可以修改它(如需詳細資訊,請參閱 進階使用量 )。

步驟 1:defaultVersion 配置

若要設定協調流程的預設版本,您必須在 Azure Functions 專案中的 defaultVersion 檔案中新增或更新 host.json 設定:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>"
    }
  }
}

版本字串可以遵循任何符合您版本設定策略的格式:

  • 多部分版本設定:"1.0.0""2.1.0"
  • 簡單編號: "1""2"
  • 以日期為基礎:"2025-01-01"
  • 自訂格式: "v1.0-release"

設定 defaultVersion之後,所有新的協調流程實例都會永久與該版本相關聯。

版本比較規則

在選取StrictCurrentOrOlder策略時(請參閱版本比對),執行階段會使用下列規則來比較協同作業實例的版本與defaultVersion工作器的值:

  • 空白或 Null 版本會視為相等。
  • 空版本或 Null 版本會視為比任何已定義版本還舊。
  • 如果這兩個版本都可以剖析為 System.Version,則使用 CompareTo 方法。
  • 否則,會執行不區分大小寫的字串比較。

步驟 2:協調器函式邏輯

若要在協調器函式中實作版本感知邏輯,您可以使用傳遞至協調器的內容參數來存取目前協調流程實例的版本,這可讓您根據版本來分支協調器邏輯。

Important

實作版本感知邏輯時,請務必保留舊版的確切協調器邏輯。 對於現有版本的活動呼叫序列、順序或簽章所做的任何變更,都可能會中斷決定性的重新執行,並導致執行中的協調流程失敗或產生不正確的結果。 部署后,舊版程式代碼路徑必須維持不變。

[Function("MyOrchestrator")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    if (context.Version == "1.0")
    {
        // Original logic for version 1.0
        ...
    }
    else if (context.Version == "2.0")
    {
        // New logic for version 2.0
        ...
    }
    ...
}

Note

屬性 context.Version唯讀的,並反映建立協調流程執行個體時永久相關聯的版本。 您無法在協調流程執行期間修改此值。 如果您想要透過 以外的 host.json方式指定版本,您可以在使用協調流程用戶端 API 啟動協調流程實例時執行此動作(請參閱 使用特定版本啟動新的協調流程和子協調流程)。

Tip

如果您剛開始使用協調流程版本設定,而且您已經有在指定 defaultVersion 之前建立的執行中協調流程,您現在仍然可以將 defaultVersion 設定新增至您的 host.json。 針對所有先前建立的協調流程,context.Version 會傳回 null (或相等的語言相依值),因此您可以建構協調器邏輯,據以處理舊版 (null 版本) 和新版本的協調流程。 以下是用於檢查舊版案例的依語言不同值:

  • C#: context.Version == nullcontext.Version is null
  • JavaScript:context.df.version == null
  • Python: context.version is None
  • PowerShell: $null -eq $Context.Version
  • Java: context.getVersion() == null 另外要注意的是,指定 "defaultVersion": null in host.json 相當於根本不指定。

Tip

視您的情況而定,您可能會偏好在不同的層級進行分支操作。 您可以在變更為必要時,精確地進行本機變更,如範例所示。 或者,您可以在更高層級建立分支,甚至在整個 orchestrator 實作層級,這可能會引入一些程式碼重複,但可能可以保持執行流程清楚。 您必須選擇最適合您案例和程式碼撰寫樣式的方法。

部署後會發生什麼事

當您使用新版本邏輯部署更新的協調器函式時,以下是預期的情況:

  • 背景工作角色共存:包含新協調器函式程式碼的背景工作角色將會啟動,而具有舊程式碼的一些背景工作角色可能仍然處於作用中。

  • 新執行個體的版本指派:新背景工作角色所建立的所有新協調流程和子協調流程都會取得從 defaultVersion 指派給他們的版本。

  • 新的背景工作角色相容性:新的背景工作角色將能夠同時處理新建立的協調流程和先前現有的協調流程,因為上一節的步驟 2 中執行的變更可確保透過版本感知分支邏輯的回溯相容性。

  • 舊背景工作角色限制:舊背景工作角色只能處理 中版本defaultVersion本身 host.json 中指定的版本協調流程,因為它們不預期具有與較新版本相容的協調器程式碼。 此限制可防止執行錯誤和非預期的行為。

Note

協調流程版本設定不會影響背景工作角色生命週期。 Azure Functions 平台會根據主機選項,依既定規則來管理運算資源的布建和解除。

範例:取代序列中的活動

此範例示範如何使用協調流程版本設定,以順序中間的不同活動取代一個活動。

1.0 版

host.json 配置:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "1.0"
    }
  }
}

協調器功能:

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();
    
    await context.CallActivityAsync("ValidateOrder", orderId);
    await context.CallActivityAsync("ProcessPayment", orderId);
    await context.CallActivityAsync("ShipOrder", orderId);
    
    return "Order processed successfully";
}

2.0 版搭配折扣處理

host.json 配置:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "2.0"
    }
  }
}

協調器功能:

using DurableTask.Core.Settings;

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();

    await context.CallActivityAsync("ValidateOrder", orderId);

    if (VersioningSettings.CompareVersions(context.Version, "1.0") <= 0)
    {
        // Preserve original logic for existing instances
        await context.CallActivityAsync("ProcessPayment", orderId);
    }
    else // a higher version (including 2.0)
    {
        // New logic with discount processing (replaces payment processing)
        await context.CallActivityAsync("ApplyDiscount", orderId);
        await context.CallActivityAsync("ProcessPaymentWithDiscount", orderId);
    }
    
    await context.CallActivityAsync("ShipOrder", orderId);

    return "Order processed successfully";
}

進階使用方式

針對更複雜的版本設定案例,您可以設定其他設定來控制運行時間如何處理版本比對和不相符的情況。

Tip

針對大部分案例使用預設組態 (CurrentOrOlder 搭配 Reject使用 ),以啟用安全的輪流部署,同時在版本轉換期間保留協調流程狀態。 只有在您有無法符合預設行為的特定需求時,才建議您繼續進行進階設定。

版本比對

versionMatchStrategy 設定會決定載入協調器函式時,執行階段如何比對編排版本。 它會控制背景工作角色可以根據版本相容性處理的協調流程執行個體。

Configuration

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionMatchStrategy": "CurrentOrOlder"
    }
  }
}

可用的策略

  • None (不建議):完全忽略編排系統版本。 不論版本為何,所有收到的工作都會進行處理。 此策略可有效地停用版本檢查,並允許任何工作者處理任何協調程序實例。

  • Strict:只處理來自協調流程的工作,其版本與 defaultVersion 在背景工作角色 host.json 中指定的版本完全相同。 此策略提供最高層級的版本隔離,但需要謹慎的部署協調,以避免孤立的協調流程。 版本 不符的後果會在版本不符處理 一節中說明。

  • CurrentOrOlder (預設值):處理來自版本小於或等於 defaultVersion 在背景工作角色 host.json 中所指定版本的協調流程工作。 此策略啟用向後相容性,使得較新的工作者能夠處理由舊版協調器啟動的編排,同時阻止較舊的工作者處理較新的編排。 版本 不符的後果會在版本不符處理 一節中說明。

版本不符合處理

versionFailureStrategy 設定會決定當協調流程實例版本不符合目前 defaultVersion時會發生什麼情況。

Configuration:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionFailureStrategy": "Reject"
    }
  }
}

可用策略:

  • Reject (預設值):不要處理編排。 編排實例會維持其目前狀態,稍後可在相容的工作角色可用時重試。 此策略是保存協調流程狀態的最安全選項。

  • Fail:調度失敗。 此策略會立即終止失敗狀態的協調流程實例,這在版本不符表示嚴重部署問題的案例中可能適用。

使用特定版本啟動新的協調流程和子協調流程

根據預設,所有新的協調流程執行個體都會使用您 defaultVersion 組態中指定的目前 host.json 來建立。 不過,您可能有需要以特定版本建立協調流程的案例,即使它與目前的預設值不同也一樣。

使用特定版本的時機:

  • 漸進式遷移:即使在部署較新版本之後,您希望能夠持續使用舊版本來建立協調流程。
  • 測試案例:您必須在生產環境中測試特定版本行為。
  • 復原情況:您必須暫時還原為使用舊版建立執行個體。
  • 版本特定的工作流程:不同的商務程式需要不同的協調流程版本。

使用協調流程用戶端 API 建立新的協調流程實例時,您可以藉由提供特定版本值來覆寫預設版本。 這可讓您更精細地控制每個新協調流程實例所使用的版本。

[Function("HttpStart")]
public static async Task<HttpResponseData> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    FunctionContext executionContext)
{
    var options = new StartOrchestrationOptions
    {
        Version = "1.0"
    };
    
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("ProcessOrderOrchestrator", orderId, options);

    // ...
}

您也可以在協調器函式中啟動具有特定版本的子協調作業:

[Function("MainOrchestrator")]
public static async Task<string> RunMainOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var subOptions = new SubOrchestratorOptions
    {
        Version = "1.0"
    };
    
    var result = await context.CallSubOrchestratorAsync<string>("ProcessPaymentOrchestrator", orderId, subOptions);
    
    // ...
}

拿掉舊版程式代碼路徑

經過一段時間,您可能會想要從協調器函式中移除舊版程式代碼路徑,以簡化維護和減少技術債務。 不過,必須小心移除程序代碼,以避免中斷現有的協調流程實例。

當移除舊版程式代碼是安全的:

  • 使用舊版的所有協調流程實例都已完成(成功、失敗或已終止)
  • 不會使用舊版本建立新的協調流程執行個體
  • 您已透過監視或查詢,確認沒有實例使用舊版執行
  • 自上次部署舊版以來,已經經過一段足夠長的時間(考慮您的業務連續性需求)

最佳移除實務準則:

  • 監視主動執行的實例:使用 Durable Functions 管理 API 來查詢使用特定版本的實例。
  • 設定保留原則:定義您想要維護每個版本的回溯相容性多久。
  • 以累加方式移除:請考慮一次移除一個版本,而不是同時移除多個版本。
  • 文件移除:維護移除版本時機和原因的明確記錄。

Warning

當協調流程實例仍在執行這些版本時移除舊版程式代碼路徑,可能會導致決定性的重新執行失敗或非預期的行為。 在移除程序代碼之前,請務必確認沒有任何實例使用舊版。

最佳做法

版本管理

  • 使用多部分版本控制:採用一致的版本設定設定,例如 major.minor.patch
  • 記錄重大變更:清楚地記錄哪些變更需要新版本。
  • 規劃版本生命週期:定義何時移除舊版程式代碼路徑。

程式碼組織

  • 不同版本的邏輯:為不同版本使用清晰的分支或個別的方法。
  • 保留決定性:避免在部署後修改現有的版本邏輯。 如果變更絕對必要(例如重大錯誤修正),請確定它們會維持具決定性的行為,且不會改變作業順序,或預期較新的協調器版本在處理較舊的協調流程時會失敗。
  • 徹底測試:測試所有版本路徑,特別是在轉換期間。

監視和可觀察性

  • 記錄版本資訊:在您的記錄中包含版本,以方便偵錯。
  • 監視版本發佈:追蹤哪些版本正在主動執行。
  • 設定警示:監視任何版本相關錯誤。

Troubleshooting

常見問題

  • 問題:部署 2.0 版之後,以 1.0 版建立的協調流程實例失敗

    • 解決方案:請確定協調器中的 1.0 版程式代碼路徑保持不變。 執行順序的任何變更都可能會中斷確定性重播。
  • 問題:執行舊版編排器的工作者無法執行新的工作流程

    • 解決方案:這是預期的行為。 執行階段會刻意防止較舊的背景工作角色使用較新版本執行協調流程,以維護安全性。 請確保所有工作者都已更新到最新的管理器版本,並且其在 defaultVersion 中的 host.json 設定都已相應更新。 如有需要,您可以使用進階組態選項來修改此行為(如需詳細資訊,請參閱 進階使用方式 )。
  • 問題:版本資訊在 Orchestrator 中不可用 (context.Versioncontext.getVersion() 為 null,不論 defaultVersion 設定為何)

    • 解決方案:檢查 [必要條件 ] 區段,以確保您的環境符合協調流程版本設定的所有需求。
  • 問題:較新版本的協調流程進展非常緩慢或完全停滯

    • 解決方案:問題可能有不同的根本原因:
      1. 較新的背景工作角色不足:請確定已在 defaultVersion 中部署含有相同或更高版本的足夠數目背景工作角色且為作用中,以處理較新的協調流程。
      2. 來自較舊背景工作角色的協調流程路由干擾:舊工作者可能會干擾協調流程路由機制,讓新背景工作角色更難挑選協調流程以進行處理。 使用特定記憶體提供者(Azure 記憶體或 MSSQL)時,這特別明顯。 一般而言,Azure Functions 平台能確保在部署後很快移除舊的工作程序,因此任何延遲通常並不顯著。 不過,如果您使用可讓您控制較舊背景工作角色生命週期的設定,請確定較舊的背景工作角色最終會關閉。 或者,請考慮使用 長期工作排程器,因為它提供較不容易發生此問題的改良路由機制。

後續步驟