IlmHamroh
JavaScript Full-stack/29-qism. Backend integratsiyalar: fayl, to'lov, bot, realtime51/69-dars15 daqiqa
Mundarija (23)

Durable workflow'lar: saga, kompensatsiya va qadam jurnali

Qisqacha: Buyurtma — uch qadam: omborda zaxira, to'lov, yetkazish. Uchinchisi yiqilsa, birinchi ikkitasini teskari tartibda bekor qilish kerak — bu saga va kompensatsiya. Jarayon ikkinchi qadamda o'lsa, qayta ishga tushganda boshidan emas, to'xtagan joyidan davom etishi kerak — bu durable execution (chidamli bajarish): har qadam natijasi jurnalga yoziladi. Kanon buni Postgres jurnali bilan sinadi: kompensatsiya teskari tartibda ishladi, o'lgan jarayon jurnaldan davom etdi. Temporal, Inngest, Trigger.dev — shu g'oyaning tayyor dvigatellari (kanon sinamagan). Ko'p hollarda BullMQ flow va idempotent qadamlar yetarli.

Bu darsda

  • Nega bir nechta servisdagi amalni bitta tranzaksiyaga sig'dirib bo'lmasligini tushunasiz.
  • Saga va kompensatsiyani yozib, teskari tartibni modelda ko'rasiz.
  • Qadam jurnali bilan "o'ldi va davom etdi" mexanizmini tushunasiz.
  • Durable dvigatellar (Temporal, Inngest, Trigger.dev) nima qilishini bilasiz.
  • Qachon dvigatel kerak, qachon BullMQ yetarli — qaror qila olasiz.

Oldin bilishingiz kerak: Transactional outbox naqshi, BullMQ ilg'or imkoniyatlari, Tranzaksiya asoslari, Idempotency-Key.

Oldingi darsda bitta bazadagi yozuv va hodisani birga qildik. Endi qadamlar turli tizimlarda va jarayon soatlab, hatto kunlab davom etadi.

1. Nega bu kerak?

Fon vazifalari bo'limining oxirgi muammosi. 38-qismdagi bozor loyihasida buyurtma shunday o'tadi:

  1. Zaxira — omborda mahsulotni band qilish (ombor jadvali).
  2. To'lov — to'lov tizimidan pul yechish (To'lov ekotizimi).
  3. Yetkazish — kuryer xizmatiga buyurtma.

Uchalasi bitta Postgres tranzaksiyasida emas: to'lov tizimi va kuryer — tashqi API'lar. Ularni ROLLBACK qilib bo'lmaydi. Kuryer xizmati javob bermasa, pul allaqachon yechilgan, mahsulot band. Nima qilamiz?

Ikkinchi muammo — vaqt. "Buyurtma berilgandan 3 kun keyin fikr so'rab email yubor" yoki "to'lov 24 soat ichida tasdiqlanmasa — bekor qil". Jarayon kunlab davom etadi, bu orada server o'nlab marta qayta ishga tushadi.

2. Saga va kompensatsiya

2.1 G'oya

Saga — uzoq amalni mahalliy qadamlar ketma-ketligiga bo'lish. Har qadamning kompensatsiyasi bor — uning ta'sirini bekor qiladigan teskari amal:

Qadam Kompensatsiya
zaxira (ombor −1) zaxirani bo'shatish (ombor +1)
to'lov (hisob −50 000) pulni qaytarish (refund, Refund va reconciliation)
yetkazish kuryer buyurtmasini bekor qilish

Qadam yiqilsa — oldingi muvaffaqiyatli qadamlar teskari tartibda kompensatsiya qilinadi. Teskari — chunki keyingi qadam oldingisiga tayanadi: avval pulni qaytaramiz, keyin zaxirani bo'shatamiz.

Kompensatsiya — "bo'lmaganday qilish" emas. Mijoz to'lov va qaytarish xabarini ko'radi, bank ikkala yozuvni saqlaydi. Saga oxir-oqibat izchillik (eventual consistency, ACID, BASE, CAP va PACELC darsida ko'rgandik) beradi: oraliqda tizim "pul yechilgan, lekin buyurtma yo'q" holatida bo'lishi mumkin, oxirida esa izchil. Nazariy tomoni — Saga: taqsimlangan tranzaksiyalar darsida.

sequenceDiagram
  participant S as Saga
  participant O as Ombor
  participant T as To'lov
  participant K as Kuryer
  S->>O: zaxira
  O-->>S: ok
  S->>T: to'lov 50 000
  T-->>S: ok
  S->>K: yetkazish
  K-->>S: xato - javob bermadi
  Note over S: kompensatsiya - teskari tartibda
  S->>T: pulni qaytar
  S->>O: zaxirani bo'shat

2.2 Model: kompensatsiya va jurnal

Holat (ombor, hisob, qadam, jurnal) — "baza"da, ya'ni jarayon o'lsa ham qoladigan obyektda. qadam — nechta qadam muvaffaqiyatli o'tgani:

js
// Saga + qadam jurnali. "baza" — jarayon o'lsa ham qoladigan holat.
const QADAMLAR = [
  { nom: "zaxira", bajar: (b) => { b.ombor -= 1; },
    qaytar: (b) => { b.ombor += 1; } },
  { nom: "tolov", bajar: (b) => { b.hisob -= 50_000; },
    qaytar: (b) => { b.hisob += 50_000; } },
  { nom: "yetkazish", bajar: (b) => {
    if (b.kuryerXato) throw new Error("kuryer javob bermadi");
  }, qaytar: () => {} },
];

function sagaYurit(b, oldirishQadami = -1) {
  for (let i = b.qadam; i < QADAMLAR.length; i++) {
    if (i === oldirishQadami) throw new Error("JARAYON O'LDI");
    try {
      QADAMLAR[i].bajar(b);
      b.qadam = i + 1;                       // jurnalga yozildi
      b.jurnal.push(`+${QADAMLAR[i].nom}`);
    } catch (e) {
      for (let k = i - 1; k >= 0; k--) {     // teskari tartibda
        QADAMLAR[k].qaytar(b);
        b.jurnal.push(`-${QADAMLAR[k].nom}`);
      }
      return `yiqildi: ${e.message}`;
    }
  }
  return "tugadi";
}

const yangi = (q = {}) =>
  ({ qadam: 0, jurnal: [], ombor: 5, hisob: 100_000, ...q });
const chiqar = (nom, r, b) => console.log(`${nom}: ${r}; ` +
  `${b.jurnal.join(" ")}; ombor ${b.ombor}, hisob ${b.hisob}`);

let b = yangi();
chiqar("muvaffaqiyatli", sagaYurit(b), b);
b = yangi({ kuryerXato: true });
chiqar("kuryer xatosi", sagaYurit(b), b);
b = yangi();
let r;
try { r = sagaYurit(b, 2); } catch (e) { r = `${e.message} → `; }
r += sagaYurit(b);                           // qayta ishga tushdi
chiqar("o'ldi va davom", r, b);

Konsolda har qatorning ikki qismiga qarang: jurnal (+ — bajarilgan qadam, - — kompensatsiya) va oxiridagi ombor, hisob:

text
muvaffaqiyatli: tugadi; +zaxira +tolov +yetkazish; ombor 4, hisob 50000
kuryer xatosi: yiqildi: kuryer javob bermadi; +zaxira +tolov -tolov -zaxira; ombor 5, hisob 100000
o'ldi va davom: JARAYON O'LDI → tugadi; +zaxira +tolov +yetkazish; ombor 4, hisob 50000
  • Muvaffaqiyatli — uch qadam, ombor 4, hisobdan 50 000 yechildi.
  • Kuryer xatosi — -tolov, keyin -zaxira: teskari tartib. Ombor va hisob boshlang'ich holatga qaytdi.
  • O'ldi va davom — jarayon uchinchi qadamdan oldin o'ldi (oldirishQadami = 2, sanoq 0 dan). Qayta ishga tushgan sagaYurit b.qadam (2) dan boshladi: zaxira va to'lov qayta bajarilmadi, faqat yetkazish. Hisobdan pul bir marta yechildi.

2.3 Kanon: Postgres jurnali bilan

Kanon xuddi shu sagani Postgres'da qildi: saga jadvali (qadam, holat, jurnal jsonb), har qadamdan keyin UPDATE saga SET qadam = …, jurnal = jurnal || …. Natijani modeldagi uch qator bilan solishtiring — jurnal ham, ombor va hisob ham bir xil chiqishi kerak:

text
muvaffaqiyatli                               tugadi; jurnal ["+zaxira","+tolov","+yetkazish"]; ombor 4, hisob 50000
yetkazish xatosi                             yiqildi: kuryer xizmati javob bermadi; jurnal ["+zaxira","+tolov","-tolov","-zaxira"]; ombor 5, hisob 100000
qadam 2 da jarayon o'ldi → qayta ishga tushdi JARAYON O'LDI → davom: tugadi; jurnal ["+zaxira","+tolov","+yetkazish"]; ombor 4, hisob 50000
Eslatma: 'durable' — qadam jurnali bazada; jarayon o'lsa davom etadi. Lekin qadam bajarilib, jurnalga yozilmay o'lsa — qayta bajariladi → har qadam idempotent bo'lsin (Temporal ham shu kafolatni beradi: activity at-least-once).

Nima bo'ldi? Bazadagi jurnal modeldagi bilan bir xil: kompensatsiya teskari tartibda, o'lgan jarayon esa qadam = 2 dan davom etdi. To'rtinchi qator — kanonning o'z eslatmasi, uni keyingi bo'limda ochamiz.

Halol izoh: kanon tajribasida jurnal va qadam bazada edi, ombor va hisob esa skript xotirasidagi obyektda — "o'lish" ham jarayonni haqiqatan to'xtatmay, xato otish bilan taqlid qilingan. Tajriba mexanizmni ko'rsatadi, ishlab chiqarish kodi emas. Haqiqiy tizimda ombor va hisob — o'z bazalaridagi yoki tashqi API'dagi holat.

3. Eng nozik joy: qadam va jurnal orasida

Kanon eslatmasidagi oxirgi gap — butun mavzuning kaliti. Qadam ikki ishdan iborat:

  1. Amalni bajarish (to'lov API'siga so'rov).
  2. "Bajarildi" deb jurnalga yozish.

Ular orasida jarayon o'lsa — pul yechilgan, jurnal esa "to'lov qilinmagan" deydi. Qayta ishga tushganda to'lov yana bajariladi. Bu yerda ham outbox dagi kabi ikki tizim (to'lov API va baza) va ularni atomik qilib bo'lmaydi. Yechim o'sha: har qadam idempotent bo'lsin. To'lov API'siga idempotentlik kalitini (buyurtma-77-tolov) bering — ikkinchi so'rov yangi to'lov yaratmaydi (Idempotency-Key). 2-mashqda farqini ko'rasiz.

Durable dvigatellar ham buni hal qilmaydi — ular faqat ochiq aytadi. Temporal hujjatida activity'lar "at-least-once" bajariladi; idempotentlik — sizning vazifangiz.

4. Durable execution dvigatellari

4.1 G'oya

Yuqoridagi qadam va jurnal ni har jarayon uchun qo'lda yozish — ko'p kod: qadamlarni saqlash, qayta urinish, taymerlar ("3 kundan keyin"), tashqi signal kutish ("admin tasdiqlasin"). Durable execution dvigatellari buni platforma qiladi. Siz oddiy async funksiya yozasiz, dvigatel har qadam natijasini saqlaydi. Jarayon o'lsa, funksiya qayta chaqiriladi va bajarilgan qadamlar qayta bajarilmay, saqlangan natijasi qaytariladi. sleep("3 days") — jarayon 3 kun xotirada turmaydi, dvigatel vaqti kelganda davom ettiradi.

Qayta ishga tushish shunday ko'rinadi:

sequenceDiagram
  participant F as Sizning funksiyangiz
  participant D as Dvigatel (saqlangan holat)
  F->>D: zaxira bajarildi, natijani saqla
  F->>D: to'lov bajarildi, natijani saqla
  Note over F: jarayon o'ldi
  D->>F: funksiyani qayta chaqiradi
  F->>D: zaxira?
  D-->>F: saqlangan natija, qayta bajarilmaydi
  F->>D: to'lov?
  D-->>F: saqlangan natija, qayta bajarilmaydi
  F->>D: yetkazish bajarildi, natijani saqla

Diagrammadagi asosiy fikr: funksiya boshidan chaqiriladi, lekin bajarilgan qadamlar natijasi dvigateldan keladi. Bu — modeldagi b.qadam va b.jurnal ning tayyor ko'rinishi.

Uchta mashhur variant. Hammasi — sinalmagan (server yoki akkaunt kerak, kanon 2026-10-27). Kod — rasmiy hujjat va namunalardan, 2026-10-28 holatiga; API versiyalar orasida o'zgaradi.

4.2 Temporal

O'z serveringiz (yoki Temporal Cloud) va TypeScript SDK. Kod ikki turga bo'linadi: workflow (deterministik orkestratsiya, tashqi dunyoga tegmaydi) va activity (haqiqiy ish — API, baza). Workflow ichida Date.now(), Math.random(), tarmoq — taqiqlangan: dvigatel workflow'ni tarixdan qayta "o'ynatadi" va natija bir xil chiqishi kerak.

ts
// workflows.ts — Temporal (hujjat bo'yicha, sinalmagan)
import { proxyActivities, sleep } from "@temporalio/workflow";
import type * as amallar from "./amallar";

const { zaxira, tolov, yetkazish, tolovQaytar, zaxiraBekor } =
  proxyActivities<typeof amallar>({
    startToCloseTimeout: "1 minute",
    retry: { maximumAttempts: 5 },
  });

export async function buyurtma(id: string): Promise<string> {
  await zaxira(id);
  await tolov(id);
  try {
    await yetkazish(id);
  } catch (e) {
    await tolovQaytar(id);
    await zaxiraBekor(id);
    throw e;
  }
  await sleep("3 days");
  return "fikr so'rash vaqti";
}

Saga — oddiy try/catch. Activity'lar dvigatel tomonidan qayta uriniladi, sleep("3 days") — haqiqatan 3 kun, jarayon esa bu orada o'chib-yonishi mumkin. Temporal'ning rasmiy namunalarida saga misoli bor (kompensatsiyalar teskari tartibda).

4.3 Inngest

Server'siz model: funksiyangiz o'z HTTP endpoint'ingizda, Inngest platformasi (yoki o'z Dev Server'i) uni qadamlar bo'yicha chaqiradi. Har step.run — saqlanadigan qadam. Inngest JS v4 da trigger triggers ichida beriladi:

ts
// Inngest v4 (hujjat bo'yicha, sinalmagan)
export const buyurtma = inngest.createFunction(
  {
    id: "buyurtma",
    retries: 3,
    triggers: [{ event: "bozor/buyurtma.yaratildi" }],
  },
  async ({ event, step }) => {
    await step.run("zaxira", () => zaxira(event.data.id));
    await step.run("tolov", () => tolov(event.data.id));
    await step.run("yetkazish", () => yetkazish(event.data.id));
    await step.sleep("fikr-kutish", "3d");
    await step.run("fikr-email", () => fikrSora(event.data.id));
  },
);

Funksiya har chaqiriqda boshidan ishga tushadi, lekin bajarilgan step.run lar saqlangan natijani darhol qaytaradi (memoizatsiya). Shuning uchun qadam id'lari ("zaxira", "tolov") noyob va barqaror bo'lishi kerak. 3-mashqda shu mexanizmni o'zingiz yozasiz.

4.4 Trigger.dev

Vazifalar Trigger.dev platformasida (bulut yoki o'z serveringiz) uzoq ishlaydi, timeout'siz:

ts
// Trigger.dev v4 (hujjat bo'yicha, sinalmagan)
import { task, wait } from "@trigger.dev/sdk";

export const buyurtma = task({
  id: "buyurtma",
  retry: { maxAttempts: 3 },
  run: async (payload: { id: string }) => {
    await zaxira(payload.id);
    await tolov(payload.id);
    await yetkazish(payload.id);
    await wait.for({ days: 3 });
    return { holat: "fikr so'rash vaqti" };
  },
});

4.5 Solishtirish

Temporal Inngest, Trigger.dev BullMQ flow + jurnal
Infratuzilma o'z server (yoki bulut) platforma (bulut yoki o'z server) Redis + Postgres (bor)
Kunlik sleep ha ha delayed ish
Holat qayerda Temporal serveri platforma o'zingizning bazangiz
O'rganish narxi yuqori (determinizm qoidalari) o'rta past
Kanonda sinalmagan sinalmagan sinalgan

5. Qachon BullMQ yetarli

Kanon xulosasi: flow + idempotent qadamlar (BullMQ ilg'or imkoniyatlari) va bazadagi holat ko'p loyihaga yetadi:

  • 2–5 qadam, soniyalar yoki daqiqalar ichida;
  • "N kundan keyin" — kechiktirilgan ish (delay) va bazadagi holat;
  • har qadam — alohida ish, qayta urinish BullMQ'da;
  • holat (qadam, jurnal) — o'z bazangizda, SQL bilan ko'rinadi.

Dvigatel kerak bo'lishining belgilari:

  • jarayonlar o'nlab qadamli, shoxlanadigan, haftalab davom etadi;
  • tashqi signal kutish (inson tasdig'i, boshqa servis hodisasi) ko'p;
  • "bu buyurtma hozir qaysi qadamda?" savoliga UI va tarix kerak;
  • jamoada bir nechta servis bir jarayonni bo'lishadi.

Yangi infratuzilma — yangi xavf: server, yangilanish, monitoring, jamoa bilimi. Kerak bo'lmaguncha qo'shmang.

Tekshirib ko'ring: "Ro'yxatdan o'tgach 1, 3 va 7-kunlarda o'rgatuvchi email yuborish". BullMQ'mi yoki durable dvigatel?

Javob

BullMQ yetarli: ro'yxatdan o'tishda uchta kechiktirilgan ish (delay 1, 3, 7 kun) va jobId = foydalanuvchi + kun. Har email ishi yuborishdan oldin bazadan tekshiradi (obunadan chiqqanmi?). Shoxlanish (masalan "1-emailni ochmagan bo'lsa — boshqa matn") ko'paysa va jarayon murakkablashsa — dvigatel haqida o'ylash vaqti.

6. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi

Bu darsda vazifalar-api ga kod qo'shilmaydi: vazifalar ilovasida ko'p bosqichli taqsimlangan jarayon yo'q. Kanon saga va jurnalni tajribada sinadi, durable dvigatellarni esa sinamadi (server/akkaunt kerak). Saga ehtiyoji 38-qismdagi bozor loyihasida — buyurtma, to'lov va yetkazishda — paydo bo'ladi; o'sha yerda to'lov moduli (tolov/, To'lov ekotizimi darslari) bilan birga quriladi.

O'zingiz sinab ko'ring: modeldagi QADAMLAR ga to'rtinchi qadam — chek (kompensatsiyasi chek bekor) qo'shing va chek yiqilganda jurnal qanday bo'lishini oldindan yozib, keyin tekshiring.

Fon vazifalari bo'limi shu bilan tugadi. Bo'lim natijasi: navbat.ts (eslatma, yetkazish, rejali ishlar) va outbox.ts + 204-outbox.sql — ikkalasi testlar bilan, vazifalar-api ga ulanishga tayyor.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Kuryer xatosidan keyin pul qaytmadi kompensatsiya yo'q har qadamga teskari amal
Pul ikki marta yechildi qadam bajarildi, jurnal yozilmadi, qayta bajarildi idempotentlik kaliti
Kompensatsiya noto'g'ri tartibda to'g'ri tartibda bekor qilingan teskari tartib
Jarayon qayta yonganda boshidan boshladi holat xotirada qadam jurnali bazada
Temporal workflow "non-determinism" xatosi workflow ichida Date.now()/tarmoq bunday ish — activity'da
Inngest qadami qayta bajarildi qadam id'si o'zgargan noyob, barqaror id
Kompensatsiya o'zi yiqildi tashqi API ishlamayapti kompensatsiya ham qayta uriniladi, oxiri — inson aralashuvi

8. Mashqlar

Mashqlar kurs/mashqlar/29/51-workflow/ da.

1-mashq (oson): bo'shliqlarni to'ldiring

  • Uzoq amalni mahalliy qadamlarga bo'lish naqshi —
  • Qadam ta'sirini bekor qiladigan teskari amal —
  • Kompensatsiyalar bajariladigan tartib —
  • Qadam natijasini saqlab, o'lgandan keyin davom etish — execution
  • Temporal'da haqiqiy ish (API, baza) bajariladigan joy —
Yechim
  • saga — har qadam o'z tranzaksiyasida.
  • kompensatsiya — refund, zaxirani bo'shatish.
  • teskari — oxirgi muvaffaqiyatli qadamdan boshlab.
  • durable — jurnal bazada yoki dvigatelda.
  • activity — at-least-once, idempotent bo'lsin.

2-mashq (o'rta): qadam ikki marta bajarilsa

To'lov qadami bajarildi, lekin jurnalga yozilmay jarayon o'ldi — qayta ishga tushganda qadam yana chaqiriladi. tolovQadami(bank, kalit, idempotent) ni yozing: idempotent bo'lsa, kalit ilgari ko'rilgan bo'lsa hech narsa qilmasin; aks holda bank.yechildi ni 50 000 ga oshirsin. Qadamni ikki marta chaqirib, kalitsiz va kalit bilan yechilgan summani chiqaring.

Yechim
js
// Qadam bajarildi, lekin jurnalga yozilmay jarayon o'ldi →
// qayta ishga tushganda qadam yana bajariladi.
function tolovQadami(bank, kalit, idempotent) {
  if (idempotent && bank.kalitlar.has(kalit)) return; // takror
  bank.kalitlar.add(kalit);
  bank.yechildi += 50_000;
}

for (const idempotent of [false, true]) {
  const bank = { yechildi: 0, kalitlar: new Set() };
  tolovQadami(bank, "buyurtma-77-tolov", idempotent); // o'ldi
  tolovQadami(bank, "buyurtma-77-tolov", idempotent); // qayta
  console.log(idempotent ? "kalit bilan:" : "kalitsiz:   ",
    bank.yechildi);
}

Konsolda:

text
kalitsiz:    100000
kalit bilan: 50000

Kalitni bank tomoni saqlaydi — sizning jarayoningiz o'lgan, uning xotirasiga tayanib bo'lmaydi. Shuning uchun to'lov tizimlari idempotentlik kalitini API darajasida qabul qiladi. Kalit biznes ma'nosidan yasaladi (buyurtma-77-tolov) — tasodifiy UUID qayta ishga tushganda boshqa bo'ladi va himoya qilmaydi.

3-mashq (qiyin): step.run memoizatsiyasi

Inngest g'oyasini yozing: step(id, fn) — xotira (Map) da id bo'lsa saqlangan natijani qaytaradi, bo'lmasa fn() ni bajarib saqlaydi (xato bo'lsa saqlamaydi). buyurtma() uch qadamdan iborat: zaxira ("Z-1"), to'lov ("T-9"), kuryer (tarmoq ishlamasa kuryer API 503, ishlasa "K-4"). Funksiyani 3 marta urining, ikkinchi urinishdan keyin tarmoq tiklansin. Har urinish natijasini va qadamlar haqiqatan necha marta bajarilganini chiqaring.

Yechim
js
// Inngest uslubidagi g'oya: funksiya har safar BOSHIDAN chaqiriladi,
// bajarilgan qadamlar natijasi "xotira"dan qaytariladi.
const xotira = new Map(); // dvigatel saqlaydigan qadam natijalari
let haqiqiy = 0;

function step(id, fn) {
  if (xotira.has(id)) return xotira.get(id);
  haqiqiy++;
  const natija = fn(); // xato otsa — xotiraga yozilmaydi
  xotira.set(id, natija);
  return natija;
}

let tarmoqIshlaydi = false;
function buyurtma() {
  const zaxira = step("zaxira", () => "Z-1");
  const tolov = step("tolov", () => "T-9");
  const kuryer = step("kuryer", () => {
    if (!tarmoqIshlaydi) throw new Error("kuryer API 503");
    return "K-4";
  });
  return [zaxira, tolov, kuryer].join(" ");
}

for (let urinish = 1; urinish <= 3; urinish++) {
  try {
    console.log(`${urinish}-urinish: ${buyurtma()}`);
    break;
  } catch (e) {
    console.log(`${urinish}-urinish: ${e.message}`);
    if (urinish === 2) tarmoqIshlaydi = true;
  }
}
console.log("qadamlar haqiqatan bajarildi:", haqiqiy);

Konsolda:

text
1-urinish: kuryer API 503
2-urinish: kuryer API 503
3-urinish: Z-1 T-9 K-4
qadamlar haqiqatan bajarildi: 5

Funksiya uch marta boshidan chaqirildi, lekin zaxira va to'lov bir martadan bajarildi — keyingi safar natija xotiradan keldi. Kuryer qadami uch marta (ikkitasi yiqildi): 1 + 1 + 3 = 5. Haqiqiy dvigatelda xotira — dvigatel serveridagi saqlanadigan holat, jarayon o'lsa ham qoladi. Qadam id'sini o'zgartirsangiz ("tolov" → "tolov-v2"), deploy'dan keyin yarim yo'ldagi jarayonlar to'lovni qayta bajaradi — id'lar shuning uchun barqaror bo'lishi kerak.

9. Real ishda

  • E-commerce buyurtmalari, bank o'tkazmalari, sayohat bron qilish (aviachipta + mehmonxona + mashina) — saga'ning klassik joylari.
  • Onboarding va marketing zanjirlari ("1, 3, 7-kun emaillari") — ko'pincha BullMQ yoki Inngest/Trigger.dev bilan.
  • AI agentlari va uzoq LLM jarayonlari — durable dvigatellarning tez o'sayotgan sohasi: qadam yiqilsa, qimmat LLM chaqiruvini qaytadan qilmaslik.
  • Intervyuda: "Mikroservislarda taqsimlangan tranzaksiyani qanday qilasiz?", "Saga va 2PC farqi?", "Kompensatsiya ham yiqilsa-chi?", "Durable execution nima?"

Xulosa

  • Bir nechta tizimdagi amalni bitta tranzaksiyaga sig'dirib bo'lmaydi — saga: mahalliy qadamlar + kompensatsiyalar.
  • Kompensatsiya teskari tartibda (kanon: +zaxira +tolov -tolov -zaxira, ombor va hisob tiklandi).
  • Qadam jurnali bazada — o'lgan jarayon to'xtagan joydan davom etadi (kanon: 2-qadamda o'ldi → jurnaldan davom).
  • Qadam bajarilib jurnal yozilmasa — qayta bajariladi: har qadam idempotent bo'lsin.
  • Temporal, Inngest, Trigger.dev — durable execution dvigatellari (sinalmagan); qadamlar memoizatsiya qilinadi, sleep kunlab.
  • Kichik va o'rta loyihaga BullMQ flow + idempotent qadamlar + bazadagi holat yetarli.

Keyingi dars: Qidiruv tizimlari qanday ishlaydi — yangi bo'lim: teskari indeks, tokenlash va BM25 — nega LIKE '%so'z%' qidiruv emas.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!