當一個或多個步驟在最終一致的操作中失敗時,利用此模式來撤銷工作。 實現複雜業務流程與工作流程的雲端託管應用程式,通常會使用遵循最終一致性模型的操作。
內容和問題
雲端應用程式經常修改分布於不同地理位置的資料來源資料。 為了避免爭用並提升分散式環境中的效能,應用程式應實作最終一致性,而非強交易一致性。 在最終的一致性模型中,一般商務作業是由一系列不同的步驟所組成。 在這些步驟中,系統狀態的整體視角可能會不一致。 但當所有步驟完成後,系統應該會恢復一致。
處理步驟失敗是最終一致性模型中一大挑戰。 失敗後,您可能需要撤銷已完成的作業步驟。 不過,你不一定能回滾資料,因為其他同時進行的應用程式實例可能會改變資料。 即使同時發生的實例不會改變資料,還原步驟可能比還原原始狀態更複雜。 你可能需要套用企業特定的規則。 舉例來說,請參考 旅遊網站的範例。
當一個實現最終一致性的操作跨越多個資料儲存庫時,你必須存取每個資料儲存庫來還原變更。 為了防止系統不一致,你必須可靠地在每個資料存儲中還原工作。
實現最終一致性的操作不一定會將受影響的資料儲存在資料庫中。 例如,在服務導向架構(SOA)環境中,操作可以呼叫服務中的動作並改變該服務所持有的狀態。 要撤銷操作,你還必須還原這個狀態變更,這可能包括再次呼叫該服務以逆轉第一個動作的效果。
解決方案
實作一個補償性交易,以撤銷原操作中已完成步驟的影響。 你可能會認為可以簡單地還原系統到原始狀態,但這種方法可以覆蓋其他並行應用程式實例的變更。 相反地,補償交易必須聰明地考慮同時進行的工作。 此流程通常依應用特定需求而異,並依原始操作而定。
你可以用工作流程來實作一個需要進行補償的最終一致操作。 當原始操作執行時,系統會記錄每個步驟的資訊以及如何解除。 若操作失敗,工作流程會逆轉已完成的步驟,並依序撤銷每一步驟。
雖然每個步驟都是獨立的行動,但它們合起來最終形成一個一致的操作。 系統必須執行每個步驟及其相應的復原操作。 若客戶取消,這些撤銷操作可作為補償交易進行。
單一步驟失敗不一定需要您透過補償性交易回滾整個系統。 例如,在旅遊網站的情境中,一位顧客訂了F1、F2和F3的機票,但未能預訂H1飯店的房間。 提供給顧客不同飯店的房間比取消航班更為可取。 客戶仍可選擇取消,這會觸發補償交易以撤銷機票預訂。 然而,這個決定應該由客戶做,而不是系統。 當決策影響深遠或難以可靠自動化時,請讓人類參與決策過程。
請考慮以下幾個重要點:
補償性交易可能不需要以原始操作的完全相反順序撤銷工作。
你或許可以並行執行一些還原步驟。
你可能需要套用企業特定的規則。 例如,取消航班預訂可能無法讓客戶獲得完整的退款。
此方法類似 Saga 分散式交易模式。
補償交易最終會成為一致的操作,可能會失敗。 系統應記錄進度,以便能從故障點恢復補償交易。 一個步驟在重試時可能會執行多次,因此將每個步驟設計為 冪等指令。
有時候,手動介入是從失敗步驟中恢復的唯一方法。 在這些情況下,系統應發出包含故障原因詳細資訊的警示。
問題和考慮
在決定如何實施此模式時,請考慮以下幾點:
在實作最終一致性的作業中的步驟失敗時,可能不容易判斷。 步驟可能不會立刻失敗,而是被阻擋。 你可能需要建立超時機制。
一般化補償邏輯並不容易。 補償性交易是應用特定性的。 它依賴應用程式擁有足夠的資訊來撤銷失敗操作中每個步驟的影響。
補償性交易不一定有效。 將補償交易中的步驟定義為冪等指令,這樣當補償交易本身失敗時,你可以重複執行。
處理步驟的基礎結構必須符合下列準則:
它在原始操作和補償交易中都具有韌性。
它不會失去補償失敗步驟所需的資訊。
它能可靠地監控補償邏輯的進度。 補償性交易會在原始操作提交後執行,其他交易可能會改變中間狀態。 因此,請確保你能夠從頭到尾對比並審核原始營運及其報酬。
補償交易不一定會在原始作業開始時將系統數據傳回其狀態。 交易會為操作在失敗前成功完成的工作量進行補償。
補償交易步驟不一定會以完全相反的順序反轉原始操作。 例如,如果某個資料儲存庫對不一致的敏感度較高,請先還原該儲存庫的變更。
有些措施可以幫助你提升成功率。 你可以對完成操作所需的每個資源設置短期鎖定並設定逾時。 你可以事先取得這些資源,然後只有在收集完所有資源後才開始工作。 在鎖定時間到期之前完成所有操作。
將更多錯誤視為暫時性的重試邏輯,有助於減少觸發補償交易的失敗。 當實作最終一致性的操作步驟失敗時,將其視為暫時異常並重試該步驟。 只有當步驟反覆失敗或無法恢復時,才停止操作並觸發補償。 欲了解更多重試策略,請參閱 暫態故障處理。
當你實施補償性交易時,你會面臨許多與實現最終一致性相似的挑戰。 如需詳細資訊,請參閱 最小化協調。
定義明確 的不可回頭點 和不可逆的步驟。 在複雜的工作流程中,你無法安全或有意義地撤銷某些操作,例如外部副作用或具法律約束力的行動。 辨識可補償與不可逆的步驟。 工作流程設計時,只有在所有關鍵驗證成功後,不可逆步驟才會發生。
使用此模式的時機
當下列情況時,請使用此模式:
一個業務操作涵蓋多個步驟、服務或資料儲存,若後續步驟失敗,必須恢復。 這種情況常見於長期執行的工作流程,這些工作流程遵循最終一致性模型,無法依賴原子交易。
故障復原通常需要特定領域的邏輯,而非簡單的資料回滾。 當需要應用商業規則時,例如取消訂位或發放部分退款,請使用補償措施。
在下列情況下,此模式可能不適用:
操作可以安全地重試,大多數失敗都是暫時性的。 在這種情況下,僅用重試邏輯通常就足夠了,而補償性交易則增加了不必要的複雜度。
系統無法容忍暫時的不一致,或補償無法可靠地恢復有效狀態。 建議在所有步驟中使用強一致性機制或原子交易。
工作負載設計
評估如何在工作負載設計中使用補償交易,以達成Azure Well-Architected框架支柱所涵蓋的目標與原則。 下表提供此模式如何支援每個要素目標的指引。
| 支柱 | 此模式如何支援支柱目標 |
|---|---|
| 可靠性 設計決策有助於使工作負載具有韌性,並確保在故障發生後能復原到正常運作的狀態。 | 補償動作透過直接回滾資料變更、破解交易鎖,甚至執行原生系統行為來逆轉關鍵工作負載路徑的故障。 - RE:02 關鍵流程 - RE:09 災害復原 |
如果此模式在一個支柱內部引入取捨,請將它們與其他支柱的目標進行考量。
範例
下圖展示了補償交易模式的 Azure 實作。 其他實作也可能符合你的工作量需求。 一個在 Azure 容器應用程式 中運行的 orchestrator,透過 Azure 服務匯流排 發送指令,協調長期運行的工作流程中的每個步驟。 每當前進步驟成功時,編排器會將執行狀態及對應的補償動作一併記錄在 Azure Cosmos DB 中,以便工作流程能夠恢復、連結和稽核。
此模型先使用重試以保持前進進度。 若步驟失敗,編排器會對暫態錯誤套用重試邏輯,並嘗試繼續原始操作。 補償僅在無法繼續進行時啟動,例如重試次數耗盡或失敗被歸類為非短暫性故障時。
業務專屬規則也可能偏好向前進展而非即時報酬。 若步驟失敗,編排器可選擇替代路徑,例如替換等效服務或備用選項,而非回滾工作流程。 對於高影響力或模糊案件,你可以暫停工作流程進行人工審查,再決定是否繼續走其他路徑或觸發報酬。 此做法將報酬視為最後手段,讓領域規則主導復原決策。
在典型的流程中,協調器會透過 服務匯流排(步驟 1 和 2)發送步驟訊息,接收成功結果,並將轉發與補償的元資料儲存在 Azure Cosmos DB 中。
你可以用兩種方式觸發賠償:
當同一工作量的後續步驟失敗,必須撤銷先前成功的步驟時, 當步驟回傳業務錯誤(如規則驗證失敗),或在技術重試用盡並將訊息移至死信佇列時,這種補償可以立即發生。
當後續客戶端明確要求取消已完成的操作時,
無論哪種情況,編排器都會讀取儲存的補償紀錄,並將補償指令傳送給相應的服務單位。 若補償步驟暫時失敗,服務匯流排 重試可在不升級事件的情況下完成該步驟。
若多次重試仍失敗,服務匯流排 會將訊息移至死符佇列並保留失敗細節。 編排器或專用死信處理器會發出警示,並向 Azure 監視器 和 Log Analytics 發送結構化遙測資料,包括故障原因與相關 ID,這些資料可顯示在 Application Insights 中。 此操作路徑協助團隊診斷故障、判斷需人工介入,並維持原始與補償流程間的可追溯性。
工作流程可自動開始對明確且低風險的狀況進行補償,或在情況模糊、影響深遠或需人工決策時暫停人工審查。
在元件間使用受管理身份及基於 Microsoft Entra ID 的授權,以避免共享秘密並強制執行最低權限存取。 當你製作簡化的參考圖時,請將這些身份與授權控制視為基線實作問題,而非明確的流程步驟。 保持圖中聚焦於調度、重試、補償和故障處理。
相關資源
微服務的資料考量:了解為何分散式系統中最終一致性與部分失效是固有的。 補償交易模式提供了一種具體機制,用以處理跨越多個服務的操作失敗。
交易外箱模式搭配 Azure Cosmos DB:當補償交易需要可靠發布事件或指令時,請使用此模式。 它有助於確保狀態變更與訊息以原子方式記錄,防止訊息遺失。
設計自我修復:將補償性交易作為自我修復應用的一部分。
排程代理監督模式:使用此模式實作跨分散式服務與資源執行業務操作的韌性系統。 這些系統有時需要補償性交易來撤銷工作。
重試模式:使用此模式處理暫時性失敗,並減少補償交易的需求。
Saga 分散式交易模式:使用此模式管理分散式交易中微服務間的資料一致性。 Saga 使用補償性交易來進行故障恢復。
管道與過濾器模式:將此模式與補償交易模式結合,作為分散式交易的替代方案,將複雜任務分解成可重複使用的步驟。