The way to Saga pattern part 1
Transaction introduction
A transaction is a single unit of logic or work, sometimes made up of multiple operations.
Transactions must be atomic, consistent, isolated, and durable (ACID).
Transactions within a single service are ACID.
In a traditional monolithic application, the system will create a local database transaction that works over multiple database tables. If any error occurs in between a transaction, it will be rolled back to its initial state. These transactions are known as ACID transactions (Atomicity, Consistency, Isolation, Durability). ACID transactions greatly ease the developer's task by having exclusive access to a particular database.
Atomicity is an indivisible and irreducible set of operations that must all occur or none occur.
Consistency means the transaction brings the data only from one valid state to another valid state.
Isolation guarantees that concurrent transactions produce the same data state that sequentially executed transactions would have produced.
Durability ensures that committed transactions remain committed even in case of system failure or power outage.
Transactions In microservice
When moving to microservices, one of the first things to realize is that individual services don’t exist in isolation.
While the goal is to create loosely coupled, independent services with as little interaction as possible, chances are high that one service needs a particular data set owned by another service, or that multiple services need to act in concert to achieve a consistent outcome of an operation in the domain of our business.
You have applied the Database per Service pattern. Each service has its own database. Some business transactions, however, span multiple service so you need a mechanism to implement transactions that span services.
A database-per-microservice model provides many benefits for microservices architectures. Encapsulating domain data lets each service use its best data store type and schema, scale its own data store as necessary, and be insulated from other services' failures. However, ensuring data consistency across service-specific databases poses challenges.
There are many “do it all, or don’t ” software applications in the real-world
If you successfully charge the user for an item but your fulfillment service reports that the item is out of stock, you’re going to have upset users if you don’t refund the charge. If you have the opposite problem and accidentally deliver items “for free,” you’ll be out of business. If the machine coordinating a machine learning data processing pipeline crashes but the follower machines carry on processing the data with nowhere to report their data to, you may have a very expensive compute resources bill on your hands.
Unlike single monolithic applications, distributed applications are dealing with multiple services. In such architectures, handling transactions could be a challenge.
领英推荐
Lets explore some patterns that helps us to solve this problem
Two-Phase Commit (2PC) Pattern
Two-Phase Commit (2PC) is one of the distributed transaction strategies that we could apply.
The 2PC Pattern is all about updating resources on multiple nodes in a single atomic operation.
In 2PC, it carries out an update in two phases.
there is a coordinator component that is in charge of controlling the transaction and containing the transaction logic. The participating nodes, which perform their local transactions, are the other component.
There are two phases. In phase one, the coordinator asks the participating nodes if they are ready to commit the transaction and waits for a yes or no response. If all nodes replied yes in phase 1, the coordinator instructs all nodes to commit; otherwise, the coordinator instructs all nodes to roll back.
2PC represents a synchronous strong consistency approach within a distributed transaction.
However, 2PC is not fully recommended for microservices-based applications due to its synchronous blocking. This protocol will need to block the required object that will be changed before the transaction completes. This prevents the relevant object from being used by a different transaction (deadlock situation) until the ongoing transaction is fully completed. This is not a good situation, especially in a modern-day application.