Skip to main content

Saga Pattern — Distributed Transactions Without 2PC

Long-running distributed transactions via a sequence of local transactions, each with a compensating rollback action.

When to use

  • Multi-service transactions (order → payment → inventory)
  • 2PC is not available or too risky for availability
  • Eventual consistency is acceptable

Tradeoffs

  • Compensating transactions add complexity (what if compensation fails?)
  • Only eventual consistency — no atomic guarantee
type Step struct {
Execute func() error
Compensate func() error
}

func runSaga(steps []Step) error {
done := []Step{}
for _, s := range steps {
if err := s.Execute(); err != nil {
for i := len(done) - 1; i >= 0; i-- {
done[i].Compensate()
}
return err
}
done = append(done, s)
}
return nil
}

Gotcha: Choreography is simpler to start but hard to trace. Orchestration is verbose but every step is visible in one place.