# Sagas pattern ###### tags: `design pattern` 用在 microservices 間的 transaction model 如何維持 consistency 參考來源: [Using sagas to maintain data consistency in a microservice architecture by Chris Richardson](https://www.youtube.com/watch?v=YPbGW3Fnmbc) [TOC] ## ACID is not option 在一個使用者購物情境中要保證, 購買商品的總額要 <= 使用者可用金錢餘額, 並建立新訂單. ### monolithic 在傳統的 monolithic 架構, 直接建立一個 transaction 在database 確認使用者的金錢餘額並建立新訂單. 符合 ACID 準則. ### microservice 在 microservice 架構中就是要把各種服務 loose Coupling. 因為每個 service 都會有自己的 database, 所以無法同時在 distribute database 使用 transaction sql. ### 2PC is not an option 雖然 2PC 可以保證 transaction 的一致性. 但是, 2PC 會遇到 * coordinator 發生問題 * 需要花許多時間去溝通(跟coordinator說這個交易在自己這邊是成功還是失敗, 收到coordinator 說可以commit 才commit) * 因為 lock 降低 throughput * 如果有 service 是用 NoSQL, 並不支援 transaction * 在 CAP 理論裡面, 2PC 會影響 availability ## Overview of sagas ### 使用 sagas 替代 2PC ![](https://i.imgur.com/zd80hfB.jpg) * sagas 模式下建立 order ![](https://i.imgur.com/tzYtgbW.jpg) ### 如何處理 rollback sagas pattern 無法像 ACID 的 transaction 一樣那麼容易做到 rollback, 需要 developer 自己寫 code 去處理. * 當 T2 失敗後就要執行 C1 來 rollback T1 成功的 transaction. ![](https://i.imgur.com/j3L1kER.jpg) ### Sagas complicate API Design 當啟用 sagas 的 transaction 時要如何做 response? * option 1: 當 saga 完成才做 response * **pros**: 可以知道結果 * **cons**: 降低 availability(因為要等待後面一連串 service 的 response) * option 2: 當 saga 建立時就先做 response (建議使用) * **pros**: 增加 availability * **cons**: client 需要透過 poll 或是被 server 通知來知道結果 ### Revised Create Order API * createOrder() * return a new orderID * 不用確認order 的內容正確性 * getOrder(id) * client 可以定期透過這個知道 order status ### Minimal impact on UI * 做非同步的處理 * 使用 popup 來告知 user 目前 order 是正在處理中. * server 使用 push notification 給 UI ## Coordinating sagas ### How to sequence the saga transaction? 在每個 transation(Ti) 做完後都需要考慮下一步: * Ti **成功**: 執行 T(i+1) * Ti **失敗**: 執行 C(i-1) #### Option1: Choreography-> ==distributed== decision making ![](https://i.imgur.com/4mPRxdQ.jpg) * 增加 service 的 coupling. order service 需要寫更多 code 去處理 coustomer service 成功或是失敗的處理. #### Option2: Orchestraction-> ==centralized== decision making ![](https://i.imgur.com/veeDGTS.jpg) 透過一個 CreateOrderSaga component 去告知每個 service 接下來要幹嘛. * 每個 service 要建立APIs 提供 Saga component 做調用. saga Orchestractor 可以視為 state machine. 根據每個 service 回傳的 status 去做下一個動作. * **Implicit Orchestractor** * Existing domain object, e.g. Order aggregate * Tradeoffs + pros: Simple - cons: Bloated responsibilites * **Explicit Orchestractor** * Dedicated object * Tradeoffs * pros: Improved separation of concern * cons: Additional class #### Event-based, implicit Orchestractor 用 event sourcing 來實作雖然容易, 但是協調者(Event Handler)需要負責很多事 ![](https://i.imgur.com/u43XAFk.jpg) #### Explicit Orchestractor 用一個 CreateOrderSaga 專門處理訂單的建立 ![](https://i.imgur.com/ZoqTSTK.jpg) **saga framework**: stateless singleton. CreateOrderSagaState: enum CreateOrderSagaData: persistent data ![](https://i.imgur.com/PasGYPe.png) ![](https://i.imgur.com/bfwna1Q.png) ![](https://i.imgur.com/N4GvFkl.png) ![](https://i.imgur.com/dBeoAFL.png) ## Transactional messaging ### Saga orchestrator <=> participant communication * Saga orchestrator 會調用 saga participant * CreateOrderSaga 會調用 saga participant 的reserveCredit(), create()/approve() * Saga participant 會丟 reply * Saga 必須要被完成, 不論成功或失敗 ### Use asynchronous messaging ### Messaging channels * Saga participant * Subscribes to its command channel * Sends to its reply channel * Saga orchestrator * Sends commands to the participant's command channel * Subscribes to the participants reply channels * (ensures ordering of all messages from that participant) ![](https://i.imgur.com/Bysps2U.png) ### Messaging must be transactional 下面這些動作 在更新 database 和傳送 messages 都要是 atomic. * Saga coordinator * Changes state * Sends commands * Processes replies * Saga participant * Process command * Updates state * Sends reply ### Use database table as a message queuue 透過建立一個 message table 來紀錄訂單的狀態, 但是會有一個問題是要如何通知 order service? ![](https://i.imgur.com/jmdxtzz.png) * Publishing message using **polling** * Message publisher periodically polls the table * Benefits: * Simple * Works well at low-scale * Drawbacks: * Latency and performance * 很難去知道這個 message 是否已經被處理過 * Transaction **log tailing** 使用 database 的 log 系統來知道是否有產生 transaction ![](https://i.imgur.com/qPpmlGl.png) * 使用 database 的特定機制去讀取 transaction log * MySQL binlog using master/slave replication protocol * DynamoDB table streams * .. * Benefits * Good performance/latency * Easy to track position * Drawbacks * Database specific * Obscure