Tizim dizayni kursi · 13-mavzu
TuzdiDinMuhammad

Distributed Transactions — xizmatlar aro izchillik

Mikroservisda bitta biznes amali bir necha xizmat va bazaga tegadi — bitta ACID tranzaksiya bularni qamrab ololmaydi. Noldan boshlab nega oddiy tranzaksiya ishlamasligini, 2PC (va nega undan qochilishini), Saga naqshini (mahalliy tranzaksiyalar + kompensatsiya), choreography vs orchestration va idempotency’ni o‘rganamiz.

Stack: NestJS · mikroservis · eventMavzular: 2PC · saga · kompensatsiya · idempotencyBog‘liq: 1-mavzu (Mikroservis), 10 (Process Manager)

Bu darsda nima bor

1–4-qismlar taqsimlangan tranzaksiyani noldan quradi: nega kerak, nega oddiy tranzaksiya ishlamaydi, 2PC va nega undan qochiladi, va Saga naqshi (mahalliy tranzaksiyalar + kompensatsiya). 5–6-qismlar — choreography vs orchestration (NestJS) va kompensatsiyaning qiyinchiliklari (idempotency, izolyatsiya). 7-qism qachon Saga kerak (va qachon to‘g‘ri chegaralar bilan uni umuman keltirmaslik) ekanini hal qiladi.

01
Distributed Tx · qism 1

Nega kerak — tranzaksiya chegaradan o‘tmaydi

🔗

Oddiy o‘xshatish: uy sotib olishda bir necha mustaqil tomon (bank, notarius, sotuvchi) ishtirok etadi. Hech biri boshqasining ishini "orqaga qaytara" olmaydi. Agar bank kreditni rad etsa — notarius allaqachon qilgan ishni alohida amal bilan bekor qilish kerak, "undo" tugmasi yo‘q.

Monolitda bitta baza bilan tranzaksiya oson: BEGIN … COMMIT, ACID hammasini "yo hammasi, yo hech nima" qiladi. Lekin mikroservisda (database-per-service, 1-mavzu) bitta biznes amali bir necha xizmat va bazaga tegadi.

Muammo

"Buyurtma ber" amali: Buyurtma xizmati yozadi → Inventar xizmati mahsulot band qiladi → To‘lov xizmati pul yechadi. Har biri alohida bazada. Agar to‘lov oxirida muvaffaqiyatsiz bo‘lsa — buyurtma yaratilgan, inventar band qilingan, lekin pul yo‘q. Endi inventar abadiy band bo‘lib qoladi. Bitta ACID tranzaksiya bularni qamrab ololmaydi.

Yechim — taqsimlangan tranzaksiya boshqaruvi

Xizmatlar aro izchillikni saqlashning maxsus yo‘llari kerak. Ikki asosiy yondashuv: 2PC (kuchli, lekin qochiladi) va Saga (mikroservis standarti — mahalliy tranzaksiyalar + kompensatsiya).

02
Distributed Tx · qism 2

Nega oddiy tranzaksiya ishlamaydi

Avval nega oddiy tranzaksiya yetmasligini aniq tushunamiz:

BuyurtmaDB 1 InventarDB 2 To‘lovDB 3 BEGIN…COMMIT bu chegaralardan o‘tolmaydi ✕
Bitta ACID tranzaksiya faqat bitta baza ichida ishlaydi

ACID tranzaksiya — bitta baza ulanishining xususiyati. Uch xil bazada uch xil ulanish bor, ularni bitta BEGIN…COMMITga bog‘lab bo‘lmaydi. Demak: yo hammasini birlashtiruvchi koordinator kerak (2PC), yo butunlay boshqa model (Saga).

Muhim tushuncha

Bu ma’lumot chegaralari (1-mavzu database-per-service)ning tabiiy oqibati. Xizmatlarni ajratganingda — ularning bazalari ham ajraladi, va tranzaksiya endi "bepul" emas.

03
Distributed Tx · qism 3

2PC va nega undan qochiladi

2PC · prepare/commit
🤝

Birinchi yechim — barcha bazalarni bitta koordinator bilan birlashtirish: 2PC (Two-Phase Commit). Ishlaydi, lekin mikroservisda undan qochiladi.

2PC qanday ishlaydi

Koordinator ikki fazada boshqaradi:

Koordinator Faza 1: "tayyormisiz?" → hamma "ha" desa DB 1 (prepare) DB 2 (prepare) DB 3 (prepare)
Faza 1: hamma "tayyor" desa → Faza 2: koordinator "commit" buyuradi (aks holda hammasi abort)

Faza 1 (prepare): koordinator hammadan "commit qila olasizmi?" deb so‘raydi, har biri tayyorlanadi va qulflaydi, "ha" deydi. Faza 2 (commit): hamma "ha" desa — koordinator "commit" buyuradi; kimdir "yo‘q" desa — hamma "abort" qiladi. Atomiklik kafolatlanadi.

Nega mikroservisda qochiladi

Bloklovchi: prepare fazasida qulflar ushlab turiladi; koordinator faza orasida o‘lsa — ishtirokchilar abadiy qulfda qolishi mumkin.
Koordinator SPOF va sinxron (sekin).
Mavjudlik pasayadi: bitta ishtirokchi sekin/o‘lik bo‘lsa — hammasi kutadi.
Bu — yuqori mavjudlikli mikroservis falsafasiga (CAP, 11-mavzu) zid. Shuning uchun zamonaviy mikroservis 2PC o‘rniga Saga tanlaydi.

04
Distributed Tx · qism 4

Saga — asosiy naqsh

saga · kompensatsiya
📿

Saga — mikroservis uchun standart yechim. G‘oyasi oddiy: bitta katta tranzaksiya o‘rniga — ketma-ket mahalliy tranzaksiyalar, va xatolikda kompensatsiya (bekor qiluvchi amallar).

Saga qanday ishlaydi

Har xizmat o‘z mahalliy ACID tranzaksiyasini bajaradi va keyingisini ishga tushiradi. Biror qadam muvaffaqiyatsiz bo‘lsa — oldingi qadamlarni teskari tartibda bekor qiluvchi (compensating) tranzaksiyalar ishga tushadi:

1. Buyurtma ✓ 2. Inventar ✓ 3. To‘lov ✕ ← kompensatsiya (teskari tartibda) Inventar bo‘shat Buyurtma bekor
Saga: qadamlar oldinga; xatolikda kompensatsiya orqaga

Muhim: bu ACID emas, eventual consistency. Oralig‘ida tizim vaqtincha nomuvofiq bo‘lishi mumkin (buyurtma bor, lekin hali tasdiqlanmagan), lekin oxir-oqibat izchil holatga keladi — yo hammasi bajariladi, yo hammasi bekor qilinadi.

Rollback emas — kompensatsiya

Farq muhim: 2PC/ACID rollback qiladi (hech narsa bo‘lmagandek). Saga’da qadam allaqachon commit bo‘lgan — uni "orqaga qaytarib" bo‘lmaydi, buning o‘rniga yangi, teskari amal qilinadi (pul yechildi → refund qilinadi, "un-charge" emas).

05
Chuqurlashtirilgan mavzu

Choreography vs Orchestration

NestJS saga

Saga’ni muvofiqlashtirishning ikki uslubi bor:

1 · Orchestration — markaziy koordinator

Bitta orkestrator qadamlarni ketma-ket chaqiradi, holatni kuzatadi va xatolikda kompensatsiyani boshqaradi. Mantiq bir joyda — murakkab jarayonlar uchun aniqroq:

// ORCHESTRATION SAGA: markaziy koordinator qadamlar + kompensatsiyalarni boshqaradi
async placeOrder(dto: CreateOrderDto) {
  const undo: (() => Promise<void>)[] = [];       // kompensatsiya stack'i
  try {
    const order = await this.orders.create(dto);            // 1-qadam
    undo.push(() => this.orders.cancel(order.id));

    await this.inventory.reserve(order.id, dto.items);      // 2-qadam
    undo.push(() => this.inventory.release(order.id));

    await this.payment.charge(order.id, dto.amount);        // 3-qadam
    undo.push(() => this.payment.refund(order.id));         // refund = kompensatsiya

    await this.orders.confirm(order.id);                    // yakuniy
    return order;
  } catch (err) {
    for (const compensate of undo.reverse()) {              // TESKARI tartibda bekor qil
      await compensate();                                   // idempotent bo'lishi SHART
    }
    throw new SagaFailedException(err);
  }
}

2 · Choreography — markaziy koordinatorsiz

Koordinator yo‘q; har xizmat eventga reaksiya qilib, keyingi eventni chiqaradi (3-mavzu). Oddiy oqimlar uchun yengil, lekin mantiq tarqoq:

// CHOREOGRAPHY SAGA: markaziy koordinator YO'Q; har xizmat eventga reaksiya qiladi
@EventPattern("order.created")
async onOrderCreated(order: Order) {
  try {
    await this.reserve(order.items);
    this.client.emit("inventory.reserved", { orderId: order.id });  // keyingi qadam
  } catch {
    this.client.emit("order.failed", { orderId: order.id });        // kompensatsiya trigger
  }
}
ChoreographyOrchestration
BoshqaruvTarqoq (event bilan)Markaziy (orkestrator)
MantiqXizmatlarga tarqalganBir joyda
QulaylikOddiy oqimlarMurakkab, ko‘p qadamli
KamchilikKuzatish/debug qiyinOrkestrator — markaz
06
Chuqurlashtirilgan mavzu

Kompensatsiya va qiyinchiliklar

idempotency · isolation
↩️

Kompensatsiya sodda ko‘rinadi, lekin amalda bir necha nozik muammo bor. Senior ularni oldindan hisobga oladi.

Kompensatsiyaning qiyin tomonlari

  • Kompensatsiya ham muvaffaqiyatsiz bo‘lishi mumkin: "inventarni bo‘shat" ham xato bersa? → retry + idempotency (14-mavzu) shart. Kompensatsiya qayta-qayta xavfsiz bajarilishi kerak.
  • Ba’zi amallarni bekor qilib bo‘lmaydi: email yuborildi, SMS ketdi — "un-send" yo‘q. Yechim: bunday amallarni saga oxiriga qo‘y (hammasi tasdiqlangach), yoki "bekor qilindi" xabari yubor.
  • Izolyatsiya yo‘q: saga davomida boshqa tranzaksiya oralig‘idagi holatni ko‘rishi mumkin (buyurtma yaratilgan, lekin hali tasdiqlanmagan). Bu — "dirty read"ga o‘xshash muammo.
Countermeasure’lar

Izolyatsiya muammosi uchun oddiy yechimlar: semantic lock (oralig‘idagi yozuvni "pending" deb belgilash — boshqalar buni ko‘rib, kutadi yoki e’tiborsiz qoldiradi), qisqa oraliq holatlar, va idempotent qadamlar. (Chuqurroq countermeasure’lar Deep qatlamda.)

Asosiy qoida — idempotency

Har bir saga qadami va har bir kompensatsiya idempotent bo‘lishi shart: retry yoki takror yuborilsa, ikki marta bajarilmasin. Buni ta’minlash uchun har amalga noyob ID va dedup (3, 9-mavzular idempotency).

07
Qaror

Qachon Saga (va qachon kerak emas)

🎯

Saga kuchli, lekin har biznes amali uchun emas. Eng yaxshi taqsimlangan tranzaksiya — ko‘pincha uning umuman kerak bo‘lmagani.

Amaliy qoidalar

1. Avval — kerakmi? Agar bog‘liq ma’lumot bitta xizmat/aggregat ichida bo‘lsa (yaxshi chegaralar, 2-mavzu), oddiy mahalliy tranzaksiya yetadi — taqsimlangan tranzaksiya kerak emas. To‘g‘ri domen dizayni ko‘p muammoni yo‘qotadi.

2. 2PC’dan qoch mikroservisda (bloklovchi, SPOF). Zarur bo‘lsa — Saga.

3. Uslub tanlash: oddiy, kam qadamli oqim → choreography; murakkab, ko‘p qadamli, kuzatuv muhim → orchestration.

4. Har qadam va kompensatsiya idempotent bo‘lsin; bekor qilib bo‘lmaydigan amallarni oxirga qo‘y.

5. Eventual consistency’ni qabul qil — foydalanuvchiga oraliq holatni ("qayta ishlanmoqda") to‘g‘ri ko‘rsat.

Bog‘lanish

Saga — 1-mavzudagi (mikroservis resilience) va 10-mavzudagi (process manager) g‘oyalarning davomi. Kompensatsiya, retry va idempotency esa 14-mavzu (Resilience Engineering) bilan chambarchas.

08
Yakuniy

Eslab qolish kerak bo‘lgan 5 jumla

  • Bitta ACID tranzaksiya faqat bitta baza ichida ishlaydi. Mikroservisda (database-per-service) biznes amali ko‘p bazaga tegadi — oddiy tranzaksiya yetmaydi.
  • 2PC (prepare + commit) atomiklik beradi, lekin bloklovchi, SPOF, sekin — mikroservisda qochiladi.
  • Saga — standart yechim: ketma-ket mahalliy tranzaksiyalar + xatolikda kompensatsiya (rollback emas — refund kabi yangi teskari amal). Eventual consistency.
  • Choreography (event, tarqoq — oddiy oqim) vs Orchestration (markaziy koordinator — murakkab oqim).
  • Qiyin tomonlar: kompensatsiya ham fail bo‘lishi, bekor qilib bo‘lmaydigan amallar, izolyatsiya yo‘qligi. Har qadam idempotent bo‘lishi shart. Eng yaxshisi — to‘g‘ri chegaralar bilan taqsimlangan tranzaksiyani umuman keltirmaslik.

Bu mavzuning Deep/Senior qatlami (2PC/3PC va XA chuqur, saga izolyatsiya anomaliyalari va countermeasure’lar — semantic lock/commutative/pessimistic view/reread/version, orchestrator state machine + persistence, saga + outbox/event sourcing, TCC — Try-Confirm-Cancel, retriable vs non-retriable xatolar, saga observability) kerak bo‘lsa — ayting.

Yoki keyingi mavzu: “14-mavzu: Resilience Engineering” — nosozliklarga chidamli tizim (timeout, retry, circuit breaker).