Skip to content

A demonstration of the Saga Choreography pattern for managing distributed transactions across microservices using Apache Kafka for event-driven communication and MySQL for data persistence.

Notifications You must be signed in to change notification settings

kumokuenchan/saga-kafka-python

Repository files navigation

Saga Choreography Pattern with Kafka and MySQL (Python)

A demonstration of the Saga Choreography pattern for managing distributed transactions across microservices using Apache Kafka for event-driven communication and MySQL for data persistence.

What is Saga Choreography?

The Saga Pattern manages distributed transactions without distributed locks by breaking them into local transactions that communicate via events.

In Choreography, there is no central coordinator. Each service:

  • Listens for events it cares about
  • Performs its local transaction
  • Publishes events for other services to react to
  • Handles compensation (rollback) when it receives failure events

Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Order Service  │     │Inventory Service│     │ Payment Service │
│                 │     │                 │     │                 │
│ Creates orders  │     │ Reserves stock  │     │ Processes pay-  │
│ Tracks status   │     │ Releases stock  │     │ ment            │
│                 │     │ (compensation)  │     │                 │
└────────┬────────┘     └────────┬────────┘     └────────┬────────┘
         │                       │                       │
         └───────────────────────┴───────────────────────┘
                                 │
              ┌──────────────────┴──────────────────┐
              │              KAFKA                  │
              │                                     │
              │  Topics:                            │
              │  - order.created                    │
              │  - inventory.reserved               │
              │  - inventory.released               │
              │  - payment.completed                │
              │  - payment.failed                   │
              └──────────────────┬──────────────────┘
                                 │
              ┌──────────────────┴──────────────────┐
              │              MYSQL                  │
              │                                     │
              │  Tables:                            │
              │  - orders                           │
              │  - inventory                        │
              │  - inventory_reservations           │
              │  - payments                         │
              └─────────────────────────────────────┘

Event Flow

Success Flow (amount < $100)

1. Order Service     → creates order (PENDING) → publishes order.created
2. Inventory Service → reserves stock → publishes inventory.reserved
3. Payment Service   → processes payment → publishes payment.completed
4. Order Service     → marks order CONFIRMED

Failure Flow with Compensation (amount >= $100)

1. Order Service     → creates order (PENDING) → publishes order.created
2. Inventory Service → reserves stock → publishes inventory.reserved
3. Payment Service   → FAILS → publishes payment.failed
4. Inventory Service → COMPENSATION: releases stock → publishes inventory.released
5. Order Service     → marks order FAILED

Quick Start

1. Start Docker Services

docker-compose up -d

Wait for MySQL to initialize (~15 seconds). Check with:

docker-compose logs saga-mysql

2. Create Virtual Environment and Install Dependencies

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

3. Run the REST API

python api.py

4. Test the Saga Pattern

Create a successful order (< $100):

curl -X POST http://localhost:5001/orders \
  -H "Content-Type: application/json" \
  -d '{"customer_id": "CUST-001", "product_id": "PROD-001", "quantity": 2, "total_amount": 50.0}'

Create a failing order (>= $100) to trigger compensation:

curl -X POST http://localhost:5001/orders \
  -H "Content-Type: application/json" \
  -d '{"customer_id": "CUST-002", "product_id": "PROD-001", "quantity": 5, "total_amount": 150.0}'

Check results:

# List all orders
curl http://localhost:5001/orders

# Check inventory levels
curl http://localhost:5001/inventory

# Check reservations (see RELEASED status for failed orders)
curl http://localhost:5001/inventory/reservations

# Check payments
curl http://localhost:5001/payments

5. Query Database Directly

# Check all tables
docker exec saga-mysql mysql -usaga_user -psaga_password saga_db -e "
  SELECT '=== ORDERS ===' AS '';
  SELECT order_id, total_amount, status, failure_reason FROM orders;
  SELECT '=== INVENTORY ===' AS '';
  SELECT * FROM inventory;
  SELECT '=== RESERVATIONS ===' AS '';
  SELECT reservation_id, order_id, quantity, status FROM inventory_reservations;
  SELECT '=== PAYMENTS ===' AS '';
  SELECT payment_id, order_id, amount, status FROM payments;
"

6. Run CLI Demo (Alternative)

python run_example.py

This runs all three scenarios automatically with detailed output.

7. View Kafka UI

Open http://localhost:8081 to see topics and messages.

API Endpoints

Method Endpoint Description
POST /orders Create new order
GET /orders/<id> Get order status
GET /orders List all orders
GET /inventory Check stock levels
GET /inventory/reservations Check reservations
GET /payments List all payments
GET /health Health check

Payment Failure Simulation

The payment service is configured to fail orders with total_amount >= $100.

Amount Result
< $100 Payment SUCCESS → Order CONFIRMED
>= $100 Payment FAILED → COMPENSATION → Order FAILED

Database Schema

orders

  • order_id, customer_id, product_id, quantity, total_amount, status, failure_reason

inventory

  • product_id, product_name, quantity

inventory_reservations

  • reservation_id, order_id, product_id, quantity, status (RESERVED/RELEASED/CONFIRMED)

payments

  • payment_id, order_id, amount, status (COMPLETED/FAILED), failure_reason

Files

File Description
docker-compose.yml Kafka + Zookeeper + MySQL + Kafka UI
init.sql Database schema and initial data
config.py Kafka configuration and topic names
database.py MySQL connection pool
events.py Event definitions (dataclasses)
order_service.py Creates orders, tracks completion/failure
inventory_service.py Reserves/releases stock with compensation
payment_service.py Processes payments, simulates failures
api.py REST API (Flask)
run_example.py CLI demo script

Ports

Service Port
Flask API 5001
Kafka 9092
MySQL 3307
Kafka UI 8081
Zookeeper 2181

Key Concepts Demonstrated

  1. Event-Driven Communication: Services communicate only via Kafka events
  2. Decoupled Services: No service calls another directly
  3. Compensation: Inventory is automatically released when payment fails
  4. Eventual Consistency: System reaches consistent state through event processing
  5. Data Persistence: All state changes are persisted in MySQL

Cleanup

docker-compose down -v

About

A demonstration of the Saga Choreography pattern for managing distributed transactions across microservices using Apache Kafka for event-driven communication and MySQL for data persistence.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages