နားလည်ထားရမယ့် အချက်
Database တစ်ခုတည်းအတွင်း transaction တစ်ခုက ACID အာမခံချက်ကို အလကားရပေးပါတယ် — database ရဲ့ transaction log ကိုယ်တိုင်က ပြောင်းလဲမှုအားလုံးကို အတူတကွ atomically commit ဒါမှမဟုတ် rollback လုပ်နိုင်ပါတယ်၊ ဘာလို့ဆိုတော့ ဒါက source of truth တစ်ခုတည်းရှိတဲ့ system တစ်ခုတည်းဖြစ်လို့ပါ။ Data ကို service ဒါမှမဟုတ် shard များစွာအတွင်း ခွဲလိုက်တာနဲ့ — order-service နဲ့ inventory-service တစ်ခုစီက ကိုယ်ပိုင် database ရှိတယ်ဆိုပါစို့ — ဒီ commit ကို coordinate လုပ်ပေးမယ့် database တစ်ခုတည်း ကျန်တော့မှာ မဟုတ်ပါဘူး၊ ဒါပေမဲ့ 'ပြောင်းလဲမှု နှစ်ခုစလုံး ဖြစ်မယ် ဒါမှမဟုတ် ဘာမှမဖြစ်ဘူး' ဆိုတဲ့ semantics ကို လိုအပ်ဆဲပါ။ Two-phase commit (2PC) က transaction idea ကို system များစွာအတွင်း တိုးချဲ့ပေးပါတယ် — coordinator တစ်ခုက participant တိုင်းကို 'prepare' လုပ်ဖို့ တောင်းဆိုပြီး (resource တွေ lock လုပ်၊ commit လုပ်နိုင်မှန်း အတည်ပြု) commit အမှန် ပြောဖို့ခင် အားလုံး သဘောတူမှန်း စောင့်ပါတယ်။ ဒါက strong atomicity ပေးပေမယ့် တကယ့်ကုန်ကျစရိတ်ရှိပါတယ် — participant အချို့ prepare လုပ်ပြီးသား ဒါပေမဲ့ commit message မပို့ရသေးခင် coordinator ပျက်သွားရင် ဒီ participant တွေဟာ lock ကို အဆုံးမရှိ ကိုင်ထားရပြီး တခြားအလုပ်တွေကို block ဖြစ်စေပါတယ်။ Saga pattern ကတော့ ဒီ strict atomicity ကို resilience နဲ့ လဲလှယ်ပါတယ် — blocking transaction တစ်ခုတည်းအစား local transaction တွေကို အစီအစဉ်တကျ လုပ်ဆောင်ပြီး service တစ်ခုစီက ကိုယ့် step ကို ချက်ချင်း commit လုပ်ပါတယ်၊ နောက် step တစ်ခု fail ဖြစ်ရင် ရှေ့က step တွေကို ပြန်ပယ်ဖျက်ဖို့ compensating action ကြိုတင်သတ်မှတ်ထားပါတယ် (ဥပမာ 'payment ကို refund ပြန်ပေးမယ်')။ ဒါကြောင့်ပဲ လက်တွေ့ microservice architecture တွေမှာ 2PC မဟုတ်ဘဲ saga တွေက လွှမ်းမိုးနေတာပါ။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Tutorial Platform student တစ်ဦးက course bundle ဝယ်တဲ့အခါ service သီးခြားသုံးခု ပါဝင်ပါတယ် - payments-service က card ကနေ ငွေကောက်ပါတယ်၊ enrollment-service က course access ပေးပါတယ်၊ notifications-service က receipt email ပို့ပါတယ် — တစ်ခုစီက ကိုယ်ပိုင် database ရှိပြီး ဒီ course ရဲ့ အစောပိုင်း lesson တွေမှာ ဖော်ပြခဲ့တဲ့ independent scaling အကြောင်းရင်းအတိအကျကြောင့် ခွဲထားတာပါ။ Payment အောင်မြင်ပေမယ့် enrollment fail ဖြစ်ရင် student ဟာ ဘာမှမရဘဲ ငွေပေးလိုက်ရတာပါပဲ။ ဒီနေရာမှာ 2PC သုံးမယ်ဆိုရင် payments-service က charge ကို lock ကိုင်ထားရမှာဖြစ်ပြီး enrollment-service နဲ့ notifications-service က ဆက်လက်လုပ်ဆောင်နိုင်မှန်း အတည်ပြုတာကို စောင့်နေရပါလိမ့်မယ် — တစ်ခုခုနှေးနေရင် ဒါမှမဟုတ် down ဖြစ်နေရင် အန္တရာယ်ရှိပါတယ်။ ဒီအစား platform က saga တစ်ခုကို သုံးပါတယ် - card ကနေ ငွေကောက်ပါ၊ ပြီးရင် access ပေးပါ၊ ပြီးရင် email ပို့ပါ၊ payment အောင်မြင်ပြီးနောက် enrollment fail ဖြစ်ခဲ့ရင် compensating refund step ကို ချိတ်ဆက်ထားပါတယ်။
အတူတူ စမ်းရေးကြည့်မယ်
TWO-PHASE COMMIT (2PC)
Coordinator Payment-svc Enrollment-svc Notify-svc
|--- PREPARE ------->| | |
|--- PREPARE --------------------->| |
|--- PREPARE ---------------------------------------->|
|<-- YES, can commit-| | |
|<-- YES, can commit-----------------| |
|<-- YES, can commit----------------------------------|
|--- COMMIT --------->| | |
|--- COMMIT -------------------------->| |
|--- COMMIT --------------------------------------->|
RISK: if coordinator dies here ^, all 3 services sit
holding locks forever, waiting for a commit that never comes.
SAGA PATTERN
Step 1: Payment-svc charges card --- SUCCESS
|
v
Step 2: Enrollment-svc grants access --- FAILS!
|
v (trigger compensation)
Compensating step: Payment-svc REFUNDS the charge
Each step commits locally and immediately.
No service ever waits, holding a lock, for another to decide.Diagram က 2PC ရဲ့ synchronous prepare/commit round-trip ဟာ coordinator က protocol ကြားထဲမှာ ပျက်သွားရင် participant အားလုံးကို block ဖြစ်စေနိုင်တာနဲ့ saga ရဲ့ local step တွေကို ချက်ချင်း commit လုပ်ပြီး နောက်ပိုင်း step တစ်ခု fail ဖြစ်ရင် compensating rollback ပြန်လုပ်တာကို နှိုင်းယှဉ်ပြထားပါတယ်။၅ မိနစ် စမ်းကြည့်
3 ဆင့်ပါဝင်တဲ့ 'subscription plan ပြောင်းခြင်း' flow အတွက် saga တစ်ခု ဒီဇိုင်းဆွဲပါ - (၁) စျေးနှုန်း ကွာခြားချက် ကောက်ခံခြင်း၊ (၂) plan tier update လုပ်ခြင်း၊ (၃) confirmation email ပို့ခြင်း။ Step (၂) fail ဖြစ်ရင် အစီအစဉ်တကျ run ဖြစ်ရမယ့် step တစ်ခုချင်းစီရဲ့ compensating action ကို ရေးပါ။
သတိလေးတစ်ချက်
Saga က ACID transaction အစစ်တစ်ခုနဲ့ တူညီတဲ့ isolation guarantee ပေးမယ်လို့ ယူဆတာ — step တွေကြားမှာ တခြား request တွေက တစ်ဝက်တစ်ပျက် state ကို မြင်နိုင်ပါတယ် (ဥပမာ payment ကောက်ပြီးသား ဒါပေမဲ့ access မရသေးတာ)၊ ဒါကြောင့် ဒီ data ကို ဖတ်တဲ့ code က atomicity ကို ယူဆမနေဘဲ intermediate state တွေကို လက်ခံနိုင်အောင် ရေးရပါတယ်။
Compensating action ကိုယ်တိုင် fail ဖြစ်နိုင်တာကို အစီအစဉ်မထားဘဲ ရေးတာ — 'ငွေပြန်အမ်းခြင်း' step က fail ဖြစ်ရင် customer ဟာ ငွေကောက်ခံခံရပြီး course access လည်းမရ၊ ငွေလည်း ပြန်မရသေးဘူးဆိုတဲ့ အခြေအနေ ဖြစ်နိုင်ပါတယ်၊ compensating action တွေမှာ တစ်ကြိမ်တည်း မျှော်လင့်ချက်နဲ့ ကြိုးစားမှုမျိုးမဟုတ်ဘဲ ကိုယ်ပိုင် retry/alerting logic လိုအပ်ပါတယ်။
Wikipedia — Two-phase commit protocol — System Design