為了保證最終一致性, 在錯誤發(fā)生時, 沿著整個錯誤的傳遞路徑進行undo操作. 對于復(fù)雜的工作流來說, 盡可能保證相互依賴的流程上數(shù)據(jù)語義的最終一致性.
問題
在分布式環(huán)境中, 大量的數(shù)據(jù)在節(jié)點間不斷的傳輸和被修改. 為了保證整個系統(tǒng)的性能, 任何一個節(jié)點往往都是要求最終一致性, 而不是全局強一致性. 在最終一致性的模式下, 分布式環(huán)境中的每個點維護自己的狀態(tài)機, 從全局來看, 整個系統(tǒng)處于不一致的狀態(tài). 但所有的節(jié)點在執(zhí)行完規(guī)定操作后, 整個系統(tǒng)可以最終達到一致的狀態(tài)(沒有新數(shù)據(jù)進來的情況下)
這種設(shè)計帶來的問題就是當一個操作在某個環(huán)節(jié)出問題了, 希望對數(shù)據(jù)進行回滾, 但是這個操作很可能無法成功. 因為上游的服務(wù)可能已經(jīng)使用了錯誤的數(shù)據(jù), 而下游服務(wù)可能還沒有把錯誤數(shù)據(jù)寫入. 回滾過程就不是簡單的達到一個正確的一致性狀態(tài), 而是達到一個業(yè)務(wù)可以容忍的一致性狀態(tài)
因此我們需要根據(jù)業(yè)務(wù)執(zhí)行一定的補償策略, 盡可能的回滾受到影響的數(shù)據(jù), 及時終止錯誤數(shù)據(jù)的傳播, 并對已經(jīng)出錯無法恢復(fù)的業(yè)務(wù)執(zhí)行一定的補償操作, 如根據(jù)日志進行非常重的重算, 或者等待新的正確數(shù)據(jù)覆蓋舊數(shù)據(jù)等.
解決
這種設(shè)計模式是和業(yè)務(wù)緊耦合的, 一般來說, 是通過維護一個業(yè)務(wù)流.系統(tǒng)會對整個業(yè)務(wù)流進行狀態(tài)記錄, 當回滾時, 針對workflow的狀態(tài)機進行回滾. 由于狀態(tài)機是維護在各個節(jié)點上的, 所以回滾過程可以并發(fā)進行.
從這個描述, 我們知道, 能夠執(zhí)行這種操作的業(yè)務(wù)必須是能夠接受最終一致性,所有操作必須是冪等的
決策
錯誤判斷 一個業(yè)務(wù)是否失敗了往往是難以判斷的, 一種情況是服務(wù)端的exception則可以判斷為立即失敗. 另外一種, 如一個預(yù)約系統(tǒng), 預(yù)約醫(yī)生后醫(yī)生因急事無法赴約, 這種業(yè)務(wù)端的失敗很難被捕捉到
補償機制難以被確定 如何回滾狀態(tài)機, 回滾過程失敗如何挽救等等都難以進行邏輯編程
非冪等操作 非冪等的操作無法回滾很容易理解, 如何把冪等操作轉(zhuǎn)化成冪等操作是一個非常大的挑戰(zhàn)
操作序列的影響 在回滾時可能又改變了很多狀態(tài), 能夠單獨回撤一個狀態(tài), 還是一起回滾, 回撤時的順序是否有影響?
示例

我們假設(shè)有這樣的一個業(yè)務(wù)場景是某旅游服務(wù)提供商, 客戶依次進行如下操作
- 訂Flight 1航班, 從北京到上海
- 訂Flight 2航班, 從上海到杭州
- 訂Flight 3航班, 從杭州回北京
- 在上海H1酒店訂一個房間
- 在杭州H2酒店訂一個房間
這一系列操作在業(yè)務(wù)上是相互關(guān)聯(lián)的, 但每個操作本身對于數(shù)據(jù)庫來說是原子的. 由于業(yè)務(wù)的異步性和延時性, 很可能客戶的所有航班和杭州的H2旅館反饋正常使用, 但上海H1旅館因為種種原因在2小時后返回失敗信息, 或者更進一步的, 返回信息表示取消之前成功的操作.
一個服務(wù)非常好的公司, 會注意到這個操作鏈, 通知用戶, 并根據(jù)用戶的意愿對關(guān)聯(lián)環(huán)節(jié)進行"回滾". 這里的"回滾"往往是基于策略的補償式的. 如發(fā)現(xiàn)用戶的航班購買成功, 但旅館失敗,則補償更高級別的同區(qū)域旅館, 旅館成功但航班失敗則補償票價并自動取消旅館的訂房.
類似這樣的業(yè)務(wù)操作, 需要能夠理解用戶意圖, 并把多個原子操作合并成相互關(guān)聯(lián)的一個大操作來面對.
同時再業(yè)務(wù)層對回滾進行策略編程, 形成基于業(yè)務(wù)的回滾機制