Saga patterns

The trouble with distributed transactions

  • In order for a distributed transaction to commit, all the participant services must be available, hence reduce availability

Using Saga pattern to maintain data consistency

  • Saga is a sequence of local transactions
  • Each local transaction updates data within a single service using familiar ACID transaction frameworks

Example of CreateOrder saga

  • The order service implements createOrder() operation using saga.
  • Saga’s first local transaction is initiated by the external request to create an order
  • The other five local transactions are each triggered by completion of the previous one

Saga use compensating transactions to roll back changes

  • The business logic can easily roll back a transaction if it detects the violation of a business rule
  • If a step failed, the application must explicitly undo the changes made by the first three steps. You must write what are known as compensating transaction

Coordinating sagas

A saga's implementation consists of logic that coordinate the steps of the saga.

There are two different ways to structure a saga's coordination logic:

  • Choreography: Distribute the decision making and sequencing among the saga participants. They primarily communicate by exchanging events.
  • Orchestration: A saga orchestrator sends command messages to saga participants telling them which operations to perform

Choreography-based sagas(event-based)

  • the participants communicate by exchange events
  • Each participant, start with OrderService, updates its database and publishes an event that triggers the next participant

The sequence of events for a CreateOrder() action is as follows:

OrderService -> create order { state: APPROVAL_PENDING }, publish OrderCreated event
ConsumerService -> consume OrderCreated, verify -> publish ConsumerVerified event
KitchenService -> consume OrderCreated event, validate Order, create ticket {
state: CREATE_PENDING
} -> publish TicketCreated
AccoutingService -> consume OrderCreated event, create a creditcard authorization {
state: PENDING
}
AccountingService -> consume TicketCreated, ConsumerVerified -> charge customer'credit card ->
publish Credit Card Authorization Failed event.
KitchenService -> consume Credit Card Authorization Failed -> change ticket state {
state: REJECTED
}
OrderService -> consume Credit Card Authorization Failed -> change order state {
state: REJECTED
}

Ensure reliability

  • Saga participants must update the database and publish events as part of a database transaction
  • Saga participants must be able to map each even that it receives to its own data. Every events much contain a correlation id

Benefits

  • Simplicity: Services publish events when they create, update, or delete business objects.
  • Loose coupling: The participants subscribe to events and don’t have direct knowledge of each other

Drawbacks

  • More difficult to understand
  • Cyclic dependencies between the services such as Order Service -> Accounting Service -> Order Service
  • Risk of tight coupling:

Orchestration(command based)

The saga orchestrator communicates with the participants using command/async reply-style interaction.

For example: Order service create an Order and Create Order Saga orchestrator.

  1. Saga orchestrator send Verify Consumer command to Consumer Service

  2. Consumer Service replies with a Consumer Verified message

  3. The saga orchestrator send Create Ticket command to Kitchen Service

  4. Kitchen Service replies with a Ticket Created message.

  5. The saga orchestrator sends an Authorize Card message to Accounting Service .

  6. Accounting Service replies with a Card Authorized message.

  7. The saga orchestrator sends an Approve Ticket command to Kitchen Service

  8. The saga orchestrator sends an Approve Order command to Order Service .

In final step, the saga orchestrator sends a command messsage to Order service, even though it's a component of OrderService.

Modeling saga orchestrators as state machines

Benefits

  • Simpler dependencies: doesn't introduce cyclic dependencies
  • Less coupling
  • Impoves seperation of concerns and simplifies business logic

Drawback

  • Centralizing too much business logic in the orchestrator

Handling lack of isolation: anamolies

  • Lost updates: One saga overwrites without reading changes made by another saga.
    • The first step of the Create Order Saga creates an Order .
    • While that saga is executing, the Cancel Order Saga cancels the Order .
    • The final step of the Create Order Saga approves the Order .
  • Dirty reads: dirty read occurs when one saga reads data that’s in the middle of being updated by another saga
    - Consumer Service —Increase the available credit.
    - Order Service —Change the state of the Order to cancelled.
    - Delivery Service —Cancel the delivery.

The structure of a Saga

  • Compensatable transactions: Transactions that can potentially be rolled back using a compensating transaction.
  • Pivot transaction: The go/no-go point in a saga. If the pivot transaction commits, the saga will run until completion
  • Retriable transactions: Transactions that follow the pivot transaction and are guaranteed to succeed.

The OrderService class

Is a domain service called by the service's API layer. It's responsible for creating and managing orders

Last updated on