Loading

How to Handle Distributed Data Consistency?

Asked by Ratul Sikder · · 9 answers · Solved

Handling distributed data consistency involves ensuring accurate, reliable synchronization across microservices with independent databases, despite network delays, failures, or eventual consistency challenges.

9 answers

Accepted answer · Answered by Mynul Islam ·

Outbox Pattern (Transactional Messaging)

Ensure that events are only published if local transaction succeeds.

✅ Process:

  1. Store event in outbox table in the same DB transaction
  2. A separate worker reads and emits the event
await db.transaction(async (tx) => {
  await tx.insert('orders', order);
  await tx.insert('outbox', orderCreatedEvent);
});
Outbox worker then emits these to Kafka/NATS.

Answered by Sahin Khan ·

Eventual Consistency via Event-Driven Architecture

Instead of tight synchronization, services publish events and react to them asynchronously.

Example (Order + Inventory):

[Order Service]
Order Created ➜ emits ➜ OrderCreatedEvent
                         ⬇
             [Inventory Service]
            Reserve Inventory

Code (Kafka + Node.js):

// Order Service
eventBus.publish('OrderCreated', { orderId, items });
// Inventory Service
eventBus.subscribe('OrderCreated', async ({ items }) => {
  reserveStock(items);
});
🧠 Pros: Decoupled, scalable
❌ Cons: Hard to debug, eventual delays

Answered by Ratul Sikder ·

Read Models via CQRS (Command Query Responsibility Segregation)

Separate write models (DB) from read models (cached/denormalized views).

Order Service ➜ emits ➜ events ➜ Projected to:
    - OrderReadDB
    - AdminDashboardDB
Fast, flexible reads while maintaining strict write consistency.

Answered by Dipa Saha ·

The Saga Pattern (Choreography & Orchestration)

Handle long-running transactions by breaking them into local transactions with compensating steps.

🔁 Two Types:

Type How It Works Choreography Services listen and react to events Orchestration One service (saga orchestrator) tells others what to do 📘 Choreography Example:

[Order Service] → emits → OrderCreated
      ⬇                          ⬇
[Inventory Service]        [Payment Service]
 emits InventoryReserved   emits PaymentProcessed
             ⬇
         [Shipping Service]

Code (Choreography in Node.js):

// Inventory Service
on('OrderCreated', reserveInventory);

// Payment Service
on('InventoryReserved', processPayment);

🧑‍✈️ Orchestration Example:

// Orchestrator
await call(orderService.createOrder)
await call(inventoryService.reserveStock)
await call(paymentService.chargeCard)
await call(shippingService.shipOrder)
🧠 Use choreography for simpler systems, orchestration for more control and rollback.

Answered by Shaykot Hossain Selim ·

Distributed Locking (when needed)

If operations must not overlap, use distributed locks with Redis, etcd, or ZooKeeper.

// Using Redlock (Redis)
const lock = await redlock.acquire(['locks:order:123'], 2000);
try {
  await processOrder();
} finally {
  await lock.release();
}
Use rarely. Overused locks reduce scalability.

Answered by Md Sharifuzzaman Shajib ·

Idempotency and Message Deduplication

Network retries and message duplication can cause duplicate operations.

Solution:

  • Use idempotency keys or deduplication IDs
// Before updating DB
if (await hasProcessed(event.id)) return;
process(event);
markAsProcessed(event.id);

Use with:

  • Kafka message keys
  • Outbox patterns
  • Redis set for seen IDs

Answered by Ratul Sikder ·

Hi

Answered by Fozle Rabbi ·

Use Versioning for Conflict Resolution (Optimistic Locking)

Let services resolve update conflicts using version numbers.

// User has version 3
UPDATE users SET name='New Name', version=4 WHERE id=1 AND version=3;
❌ Fails if version mismatch, preventing overwrites.

Answered by John Errik ·

Helpful

Browse more questions from the manob.ai community