๐ฐ Transactions & Sagas in Distributed Systems
Welcome back to The Code Hut Distributed Systems series! In this post, we’ll discuss how to handle transactions that span multiple services or nodes — a common challenge in distributed systems. ๐
⚠️ The Problem with Distributed Transactions
In a single database, transactions are straightforward: ACID guarantees ensure consistency. But in a distributed system:
- ๐ ️ Multiple services may need to update different databases
- ๐ Network failures can occur mid-transaction
- ⏳ Traditional two-phase commit (2PC) can be slow and complex
๐ก The Saga Pattern
The Saga pattern manages distributed transactions as a series of local transactions:
- Each service performs a local transaction
- If a step fails, compensating transactions undo previous steps
- Ensures eventual consistency without blocking resources
๐️ Saga Example
Below is a Saga involving five services, each with a normal action and a compensating action. This mirrors a real checkout flow but stays simple and framework-free.
๐ฆ Services Involved
- OrderService – creates and cancels orders
- PaymentService – charges and refunds customers
- InventoryService – reserves and releases stock
- ShippingService – books and cancels shipments
- NotificationService – sends and retracts notifications
๐งฉ Service Implementations (Simple Java)
class OrderService {
void create(Order order) {
System.out.println("Order created");
}
void cancel(Order order) {
System.out.println("Order cancelled");
}
}
class PaymentService {
void charge(Order order) {
System.out.println("Customer charged");
}
void refund(Order order) {
System.out.println("Payment refunded");
}
}
class InventoryService {
void reserve(Order order) {
System.out.println("Stock reserved");
}
void release(Order order) {
System.out.println("Stock released");
}
}
class ShippingService {
void scheduleShipment(Order order) {
System.out.println("Shipment scheduled");
}
void cancelShipment(Order order) {
System.out.println("Shipment cancelled");
}
}
class NotificationService {
void sendConfirmation(Order order) {
System.out.println("Confirmation email sent");
}
void unsendConfirmation(Order order) {
System.out.println("Confirmation email retracted");
}
}
๐ง Full Saga Orchestrator
The Saga orchestrator runs the workflow and ensures compensations are applied in reverse order if anything fails.
class CheckoutSaga {
private final OrderService orderService = new OrderService();
private final PaymentService paymentService = new PaymentService();
private final InventoryService inventoryService = new InventoryService();
private final ShippingService shippingService = new ShippingService();
private final NotificationService notificationService = new NotificationService();
void run(Order order) {
try {
orderService.create(order);
paymentService.charge(order);
inventoryService.reserve(order);
shippingService.scheduleShipment(order);
notificationService.sendConfirmation(order);
System.out.println("Checkout completed successfully!");
} catch (Exception e) {
System.out.println("Saga failed: " + e.getMessage());
// Compensation chain (reverse order)
safe(() -> notificationService.unsendConfirmation(order));
safe(() -> shippingService.cancelShipment(order));
safe(() -> inventoryService.release(order));
safe(() -> paymentService.refund(order));
safe(() -> orderService.cancel(order));
System.out.println("Compensations executed. System consistent.");
}
}
private void safe(Runnable action) {
try { action.run(); } catch (Exception ignored) {}
}
}
▶️ Running the Saga
public static void main(String[] args) {
CheckoutSaga saga = new CheckoutSaga();
Order order = new Order(); // placeholder
saga.run(order);
}
๐งญ What This Example Shows
- A realistic multi-step distributed workflow
- Each service performs a local transaction
- Compensations undo side effects when needed
- No external dependencies or frameworks — just simple, clear Java
๐ Orchestrated vs. Choreographed Sagas
๐ฎ Orchestration (central controller)
One service coordinates all steps and compensations.
Pros: easier to reason about, centralized logic
Cons: orchestration service may become complex
๐ผ Choreography (event-driven)
Each service publishes events and listens for others. E.g.:
- OrderCreated → PaymentRequested → StockReserved → ShippingScheduled
Pros: highly decoupled
Cons: logic is distributed across many services
๐ When to Use the Saga Pattern
- Microservices updating different databases
- Systems needing eventual consistency
- Long-running workflows (payments, reservations, travel booking)
- Anti-pattern: tightly coupled ACID-like workflows (use a monolith instead)
✅ Benefits of Sagas
- ❌ No need for distributed locks across services
- ⚡ Scales well with microservices
- ๐ Supports eventual consistency
- ๐ Reduces coupling between services
Next in the Series
In the next post, we’ll explore Fault Tolerance & Reliability, including patterns like replication, retries, and idempotency in distributed Java systems.
Label for this post: Distributed Systems
Comments
Post a Comment