System Design শেখো
শেখো / আর্কিটেকচারাল প্যাটার্ন

Saga & Distributed Transactions

12 মিনিট Module 3 · Patterns
এক নজরে
  • একাধিক সেবা জুড়ে একটা transaction চালানো কঠিন — 2-Phase Commit scale-এ ভঙ্গুর ও slow।
  • Saga pattern বড় transaction-কে অনেক ছোট local transaction-এ ভাগ করে, ব্যর্থ হলে compensating transaction দিয়ে আগেরগুলো undo করে।
  • Saga দুই ধাঁচে চলে — choreography (event-ভিত্তিক) ও orchestration (কেন্দ্রীয় coordinator); idempotency অপরিহার্য।

সমস্যাটা কী?

একটা অর্ডারের কথা ভাবো যা চারটা আলাদা সেবা ছুঁয়ে যায়: Order → Payment → Inventory → Shipping। একটা monolith-এ এই চারটা একই database-এ থাকত, তাই একটা transaction-এ মুড়ে দিলেই হতো — সব সফল হলে commit, যেকোনো একটা ব্যর্থ হলে পুরোটা rollback। সহজ।

কিন্তু microservices-এ প্রতিটি সেবার নিজস্ব database। এখন প্রশ্ন: payment কেটে গেছে, কিন্তু inventory-তে দেখা গেল পণ্য শেষ — তাহলে? টাকা তো কেটে নিয়েছ, পণ্য দিতে পারবে না। একাধিক database জুড়ে একটা পরমাণু (atomic) transaction চালানোই হলো distributed transaction-এর মূল চ্যালেঞ্জ।

মূল ধারণা

একটা Distributed Transaction হলো এমন একটা কাজ যা একাধিক স্বাধীন সেবা/database জুড়ে চলে, অথচ চায় হয় সব সফল হোক, নয় কিছুই না হোক। Saga pattern এই সমস্যার একটা ব্যবহারিক সমাধান — বড় transaction-কে অনেক ছোট local transaction-এর শৃঙ্খলে ভাগ করা।

কেন 2PC যথেষ্ট নয়

প্রথাগত সমাধান Two-Phase Commit (2PC): একজন coordinator আগে সবাইকে জিজ্ঞেস করে "তোমরা প্রস্তুত?" (prepare), সবাই হ্যাঁ বললে "এবার commit করো" বলে। সমস্যা:

  • এটি lock ধরে রাখে — সবাই উত্তরের অপেক্ষায় resource আটকে রাখে, ফলে slow।
  • coordinator ক্র্যাশ করলে সবাই আটকে (blocked) যায়।
  • scale ও high-latency নেটওয়ার্কে এটা ভঙ্গুর।

তাই বড় distributed system-এ 2PC প্রায় এড়িয়ে চলা হয়।

কীভাবে কাজ করে

Saga = ছোট ছোট ধাপ + undo

Saga-তে প্রতিটি সেবা নিজের local transaction করে commit করে, তারপর পরের সেবাকে এগিয়ে যাওয়ার সংকেত দেয়। কোনো ধাপ ব্যর্থ হলে আগের সফল ধাপগুলো একটা Compensating Transaction দিয়ে "উল্টে" দেওয়া হয় — কারণ commit হয়ে যাওয়া জিনিস rollback করা যায় না, তাই বাতিল করতে বিপরীত কাজ করতে হয়।

ধাপমূল কাজCompensating (undo) কাজ
Orderঅর্ডার তৈরিঅর্ডার বাতিল
Paymentটাকা কাটাটাকা ফেরত (refund)
Inventorystock কমানোstock ফেরত যোগ
Shippingshipment বুকshipment বাতিল

ধরো Shipping ধাপে গিয়ে courier পাওয়া গেল না — তখন উল্টো দিকে compensating চলে: Inventory ফেরত, Payment refund, Order বাতিল।

Choreography বনাম Orchestration Saga

দিকChoreography SagaOrchestration Saga
নিয়ন্ত্রণকেন্দ্র নেই, event-ভিত্তিকএকটা কেন্দ্রীয় orchestrator
সেবা কীভাবে জানেevent শুনে নিজে reactorchestrator নির্দেশ দেয়
সরল flow-তেচমৎকার, হালকাএকটু overkill
জটিল flow-তেবোঝা কঠিন, ছড়ানোflow এক জায়গায়, পরিষ্কার
failure সামলানোবিকেন্দ্রীভূত, জটিলকেন্দ্রীয়, সহজ

Choreography-তে Payment শেষ হলে PaymentCompleted event ছাড়ে, Inventory সেটা শুনে কাজ শুরু করে — কেউ কাউকে সরাসরি ডাকে না। Orchestration-এ একটা Order Orchestrator এক এক করে সবাইকে বলে "এবার payment করো", "এবার stock কমাও" — আর ব্যর্থ হলে সে-ই compensation চালায়।

Idempotency কেন অপরিহার্য

Network-এ message হারায়, retry হয়, duplicate আসে। তাই প্রতিটি ধাপ Idempotency মানতে হবে — একই নির্দেশ দুবার এলেও যেন একবারের মতোই ফল হয় (যেমন একই payment ID-তে দুবার call এলেও একবারই টাকা কাটবে)। সাধারণত প্রতিটি transaction-কে একটা unique ID দিয়ে আগে চেক করা হয় "এটা কি আগেই হয়ে গেছে?"।

সহজ উদাহরণ

বিয়ের আয়োজন ভাবো — কমিউনিটি সেন্টার বুক, ক্যাটারিং অর্ডার, কার্ড ছাপা, ফটোগ্রাফার বুক। প্রতিটি আলাদা দোকানে আলাদা advance দিয়ে confirm করো (ছোট ছোট local transaction)। এখন যদি শেষ মুহূর্তে কমিউনিটি সেন্টার বাতিল হয়ে যায়, তুমি তো এক জাদুতে সব ফিরিয়ে আনতে পারো না — তোমাকে এক এক করে ফোন করে ক্যাটারিং বাতিল করতে হয়, কার্ডের অর্ডার থামাতে হয়, advance ফেরত চাইতে হয়। এই উল্টো-কাজগুলোই compensating transaction। আর তুমি একজন ইভেন্ট ম্যানেজারকে দিয়ে সব সমন্বয় করালে সেটা orchestration; নিজে নিজে এক দোকান আরেক দোকানকে জানালে choreography।

সুবিধা ও অসুবিধা

সুবিধা:

  • কোনো long-lived lock নেই: প্রতিটি local transaction দ্রুত commit হয়, তাই scalable।
  • Loose coupling (বিশেষত choreography-তে): সেবাগুলো স্বাধীন থাকে।
  • Resilient: এক সেবা ডাউন থাকলেও পরে retry করে এগোনো যায়।
  • microservices-এর সাথে মানানসই: database-per-service-কে সম্মান করে।

অসুবিধা:

  • কোনো সত্যিকারের rollback নেই: compensation নিজে লিখতে হয়, যা জটিল ও ভুল-প্রবণ।
  • Eventual consistency: মাঝপথে সিস্টেম অসম্পূর্ণ অবস্থায় থাকতে পারে।
  • Choreography-তে debugging কঠিন: flow ছড়ানো।
  • Idempotency ও duplicate handling বাধ্যতামূলক — না হলে double charge।
  • জটিল failure case: compensation নিজেই ব্যর্থ হলে কী হবে? — সেটাও ভাবতে হয়।

কখন ব্যবহার করবে / করবে না

ব্যবহার করবে যখন: একটা business প্রক্রিয়া একাধিক সেবা/database ছুঁয়ে যায়, strong atomic consistency সম্ভব নয়, আর eventual consistency গ্রহণযোগ্য।

এড়িয়ে যাবে যখন: সব data একটা database-এ থাকে (তখন সাধারণ ACID transaction-ই যথেষ্ট), কিংবা ব্যবসায়িকভাবে কোনো মধ্যবর্তী অসংগত অবস্থা একদমই সহ্য করা যাবে না।

সাবধান

সবচেয়ে বিপজ্জনক ভুল: compensating transaction-কে পরে ভাবব বলে ফেলে রাখা। মনে রাখো — প্রতিটি ধাপের জন্য একটা নির্ভরযোগ্য undo আগে থেকে ডিজাইন করতে হবে, এবং compensation নিজেও idempotent ও retry-যোগ্য হতে হবে। "happy path" সহজ, কিন্তু Saga-র আসল কাজ হলো failure path নিখুঁত করা। এটা অবহেলা করলে টাকা কেটে নিয়েও পণ্য না দেওয়ার মতো বিপর্যয় ঘটবে।

বাস্তব উদাহরণ

যেকোনো বড় food-delivery বা e-commerce — যেমন foodpanda/Daraz-ধাঁচের সিস্টেম — order flow-তে Saga ব্যবহার করে। তুমি অর্ডার দিলে: Order service অর্ডার বানায়, Payment service টাকা কাটে, Inventory/Restaurant service item নিশ্চিত করে, Delivery service rider বরাদ্দ করে। কোনো ধাপ ব্যর্থ হলে (যেমন রেস্টুরেন্ট বন্ধ) — Payment স্বয়ংক্রিয়ভাবে refund হয়, অর্ডার বাতিল দেখায়।

Uber-এর payment ও trip flow, এবং অনেক banking সিস্টেমের fund transfer (account A থেকে debit, account B-তে credit, ব্যর্থ হলে reverse) — সবই Saga-র বাস্তব রূপ। অনেকে এর জন্য Temporal বা Camunda-র মতো orchestration engine ব্যবহার করে।

টিপস

ইন্টারভিউতে "distributed transaction কীভাবে সামলাবে?" জিজ্ঞেস করলে কখনো শুধু "2PC" বোলো না। বলো: "বড় scale-এ 2PC এড়িয়ে Saga ব্যবহার করব — local transaction + compensating transaction দিয়ে, আর প্রতিটি ধাপ idempotent বানাব।" তারপর choreography vs orchestration-এর trade-off যোগ করলে তুমি গভীরতা দেখাও।

মিনি কুইজ

1. Two-Phase Commit (2PC) microservices-এ কেন কম ব্যবহৃত হয়?

2. Saga-তে কোনো ধাপ ব্যর্থ হলে কী করা হয়?

3. Saga-তে idempotency কেন জরুরি?