2 min read

Saga: distributed transactions pattern

Saga: distributed transactions pattern

Sometimes we need to modify data atomically in our database. For example, imagine a banking application where you make a $100 transfer from your account to your friend’s account. This operation means that the application has to subtract $100 from your balance and add $100 to theirs. This operation must be atomic—that means the database applies both sub-operations or neither. It can’t apply just one and “create money out of thin air,” nor drop money altogether.

Common single-machine relational databases like MySQL or PostgreSQL provide ACID guarantees:

  • Atomicity. All operations succeed or no operations succeed.
  • Consistency. Data transits from one valid state to another valid state.
  • Isolation. Concurrent transactions get the same results as sequential transactions.
  • Durability. Changes persist after they are commited, even when failure occurs.

This works perfectly in monolithic applications, but not in distributed systems. In a microservice architecture, each service typically has its own database. That prevents you from using traditional transactions across services; one solution to this limitation is the Saga pattern.

What is a Saga?

Saga is design pattern which maintains the data consistency in distributed systems.

⚠️
Saga couldn't provide strong consistency, it provides only eventual consistency.

How does it works?

Let’s imagine a travel website where you want to book both a flight and a hotel—because you generally don’t want one without the other. Though this looks like a single transaction (“book hotel and buy tickets, or do nothing”), our microservice-based site is split into three services we must coordinate:

  • Guest service (stores people who make bookings)
  • Ticket service (reserves flight tickets)
  • Hotel booking service

To complete the overall “transaction,” we send three separate requests—one to each service—and then either confirm the booking if all succeed, or roll everything back if any step fails.

💡
The Saga pattern relies on compensating transactions, which undo previous steps when a later step fails.

A simple Saga algorithm looks like this:

  1. Register the guest.
  2. Try to reserve tickets.
    1. If reservation fails, compensate by unregistering the guest.
  3. Try to book the hotel.
    1. If booking fails, compensate by unregistering the guest and canceling the ticket reservation.

Any step in this sequence can be executed asynchronously—via message queues instead of direct RPC calls—so occasionally users may observe intermediate states until the Saga completes.