Mastering Data Consistency Across Microservices
Mastering Data Consistency Across Microservices
Microservices architecture is a software design pattern where an application is built as a collection of small, independent services, each responsible for a specific function.
These services communicate with each other using APIs (Application Programming Interfaces) and operate independently, allowing for greater flexibility, scalability, and ease of maintenance. Think of a food delivery app with the following services:
- The order service manages customer orders.
- The payment service handles transactions.
- The restaurant service updates menu availability.
- The delivery service assigns and tracks deliveries.
Each service operates independently, allowing teams to update or scale them separately.
Challenges of Data Consistency
Due to the separation of services, a major challenge with microservices is maintaining data consistency. Unlike monolithic systems, where all functionalities share a single database, microservices have independent databases, leading to challenges such as:
- Duplicate or Lost Data – Without proper coordination, data may be duplicated across multiple services or lost due to partial updates.
- Network Delays – Communication between services happens over the network, which introduces latency and failure risks.
- Concurrency Issues – Simultaneous updates by different services can lead to conflicting data states.
How Data Inconsistency Arises
1. Eventual Consistency vs. Strong Consistency
Microservices rely on eventual consistency, meaning that data updates propagate over time instead of instantly. However, in critical operations like financial transactions, strong consistency is required.
2. Distributed Transactions
Unlike monolithic systems where transactions update multiple tables atomically, microservices need a way to ensure consistency across multiple independent databases.
3. Network and Service Failures
Failures in a service or network delays can cause stale or incomplete information in dependent services.
4. Concurrent Updates
When multiple instances of the same service update shared data simultaneously, race conditions can cause conflicts.
Strategies for Ensuring Data Consistency
1. Saga Pattern (Compensating Transactions)
A saga is a sequence of transactions where each step updates a different microservice, and compensating transactions roll back changes in case of failure. Two types of sagas:
- Choreography (فن تصميم الرقصات) – Choreography provides to coordinate sagas with applying publish-subscribe principles. With choreography, each microservices run its own local transaction and publishes events to message broker system and that trigger local transactions in other microservices.Although Choreography way decouple direct dependency of microservices when managing transactions, if Saga Workflow steps increase, then it can become confusing and hard to manage transaction between saga microservices.
- Orchestration – provides to coordinate sagas with a centralized controller microservice. This centralized controller microservice, orchestrate the saga workflow and invoke to execute local microservices transactions in sequentially.
The orchestrator microservices execute saga transaction and manage them in centralized way and if one of the step is failed, then executes rollback steps with compensating transactions.
Example in a food delivery app:
- The order service creates an order.
- The payment service processes payment.
- The restaurant service confirms availability.
- If any step fails, previous actions are rolled back (e.g., refunding payment).
2. Two-Phase Commit (2PC)
Ensures all participating services confirm a transaction before it is committed. However, it's less common due to its blocking nature, which reduces scalability.
3. Event-Driven Architecture
Microservices communicate asynchronously by publishing and subscribing to events via a message broker (e.g., Kafka, RabbitMQ). This ensures eventual consistency.
4. Idempotency and Retries
Microservices should ensure that retrying a request does not create duplicate data. Using idempotency keys ensures repeated transactions have the same effect as a single one.
⚙️ How It Works
1. Client Sends a Request
The client includes a unique idempotency key in the HTTP header or request body:
POST /api/orders
Idempotency-Key: 4a1e6bcd-9f92-42b3-b6dd-7e8899934a91
2. Server Behavior:
-
Checks if it has already processed a request with this key.
-
✅ If yes → Returns the same result as before (cached response).
-
❌ If no → Processes the request, stores the result, and returns the response.
-
5. Optimistic Concurrency Control
Versioning (e.g., timestamps or version numbers) helps detect and resolve concurrent modifications before updating records.
🔹 Step 1: Create the Table Contains a "Timestamp Column"
SELECT ProductID, ProductName, Quantity, RowVerFROM ProductsWHERE ProductID = 1;
UPDATE ProductsSET Quantity = 8WHERE ProductID = 1AND RowVer = 0x00000000000007D3;
🔹 Step 4: Check for Success or Concurrency Conflict
IF @@ROWCOUNT = 0PRINT 'Update failed: concurrency conflict detected.';ELSEPRINT 'Update succeeded.';
Conclusion
Maintaining data consistency in a microservices architecture is challenging but manageable with the right strategies. While monolithic applications rely on strong consistency through single database transactions, microservices embrace eventual consistency using sagas, event-driven communication, retries, and concurrency control mechanisms. By carefully designing data interactions and handling failures proactively, developers can build resilient and scalable microservices-based applications.
Comments
Post a Comment