Saga у світі розподілених систем — це патерн, який допомагає керувати довгоживучими транзакціями (ті самі, що відбуваються у кількох мікросервісах в одному процесі). Якщо спростити, це ланцюжок послідовних кроків, що виконує локальні транзакції в різних сервісах, забезпечуючи їхню атомарність через компенсаційні дії.
Для моноліту транзакції ACID (Atomicity, Consistency, Isolation, Durability) — звична справа. Але в мікросервісній архітектурі це, на жаль, як квадратні колеса на велосипеді: нібито щось їде, але не так, як хотілося б. Ось тут на арену виходить Saga: вона підтримує eventual consistency (поступову консистентність), що більш реалістично у світі мікросервісів.
Історія та причини виникнення
Патерн Saga вперше з'явився в 1987 році завдяки роботам Гектора Гарсія-Моліни та Кеннета Б. Кіма. Його основна ідея полягала в можливості розбиття довгоживучої транзакції на серію керованих операцій з можливістю компенсації. Звучить складно? Уяви ресторан, який приймає замовлення (локальна транзакція), але якщо інгредієнти для страви закінчилися, тобі пропонують скасувати замовлення або вибрати іншу страву (компенсаційна дія).
Коли мікросервіси стали мейнстрімом, Saga повернулася як спосіб координації розподілених транзакцій. І фреймворк Spring Boot, до речі, надає багато інструментів для її реалізації.
Як працює Saga?
Saga ділиться на кілька кроків. Вони виконуються послідовно або паралельно, і кожен крок включений у локальну транзакцію. Суть проста:
- Починається перший крок (наприклад, резервування товару).
- Якщо він успішний, переходиш до другого кроку (скажімо, списання грошей).
- Якщо на якомусь кроці стався збій (ой, грошей на карті нема), запускаються компенсаційні транзакції (скасування резервування товару).
Патерн Saga фокусується на двох речах: основні дії (наприклад, купівля квитка) і компенсаційні дії (повернення грошей, якщо квиток не куплений).
Приклади використання
Приклад 1. Бронювання поїздки
- Ти бронюєш рейс, готель і оренду авто.
- Якщо бронювання готелю не вдалося, потрібно скасувати бронь рейсу і оренду авто.
Приклад 2. Купівля товару в інтернет-магазині
- Зменшення залишків на складі.
- Списання коштів з картки.
- Відправка повідомлення на email.
- Якщо якийсь крок не вдався, потрібно повернути гроші і скасувати замовлення.
Переваги Saga
Saga допомагає підтримувати баланс між гнучкістю розподіленої системи і необхідністю зберігати послідовність даних. Ось кілька причин, чому вона важлива:
- Консистентність даних: Saga вирішує проблему, коли система має зберегти узгодженість даних, незважаючи на помилки.
- Гнучкість: Saga працює як для синхронних, так і для асинхронних викликів.
- Масштабованість: у кожного сервісу є свій локальний стан, що не залежить від транзакційного менеджера.
- Простота відновлення: якщо щось пішло не так, достатньо запустити компенсаційні дії.
Ще один приклад
Уявімо, що твій інтернет-магазин побудований на базі мікросервісів з використанням Saga:
- Товарний сервіс: резервує товар.
- Сервіс оплати: списує гроші.
- Сервіс доставки: генерує замовлення для кур'єра.
Покупець додає товар у кошик, оплачує, чекає доставку, все йде гладко… поки кур'єр не сказав: "Вибачте, адреса доставки не існує". Saga тут рятує: сервіс доставки каже товарному сервісу повернути товар, а платіжному — повернути гроші. Все, клієнт задоволений.
Обмеження?
Saga, звісно, крута, але ідеальних рішень не буває. Ось що варто врахувати:
- Немає миттєвої консистентності: Saga забезпечує eventual consistency, що може бути неприйнятним для критично важливих даних.
- Складність реалізації: чим більше кроків у бізнес-процесі, тим складніше керувати saga.
- Компенсаційні дії: їх часто важко проєктувати і тестувати (кумедний факт: скасувати "доставку піци" легше, ніж скасувати "запуск ракети").
Приклад використання патерну Saga у коді
Для наочності давайте реалізуємо базовий сценарій: бронювання товару в інтернет-магазині, оплата і доставка. Наш процес виглядатиме так:
- Резервування товару.
- Оплата замовлення.
- Генерація доставки.
Крок 1: Визначимо події
public class ReserveProductCommand {
private String productId;
private int quantity;
private String orderId;
// Конструктори, геттери і сеттери
}
public class PaymentCommand {
private String orderId;
private double amount;
// Конструктори, геттери і сеттери
}
public class ShipOrderCommand {
private String orderId;
private String address;
// Конструктори, геттери і сеттери
}
Крок 2: Резервування товару
@Service
public class InventoryService {
public void reserveProduct(ReserveProductCommand command) {
// Логіка резервування товару
System.out.println("Резервуємо продукт " + command.getProductId());
}
public void cancelReservation(String productId) {
// Компенсаційна транзакція
System.out.println("Скасування резервування продукту " + productId);
}
}
Крок 3: Оплата товару
@Service
public class PaymentService {
public void processPayment(PaymentCommand command) {
// Логіка списання коштів
System.out.println("Списуємо " + command.getAmount() + " для замовлення " + command.getOrderId());
}
public void refundPayment(String orderId) {
// Компенсаційна транзакція
System.out.println("Повернення коштів для замовлення " + orderId);
}
}
Крок 4: Організація Saga
@Service
public class OrderSagaService {
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
public void processOrder(String orderId, String productId, int quantity, double amount) {
try {
// Крок 1: Резервування товару
inventoryService.reserveProduct(new ReserveProductCommand(productId, quantity, orderId));
// Крок 2: Оплата
paymentService.processPayment(new PaymentCommand(orderId, amount));
// Крок 3: Генерація доставки
System.out.println("Генерація доставки для замовлення: " + orderId);
} catch (Exception ex) {
// Обробка помилок і запуск компенсацій
inventoryService.cancelReservation(productId);
paymentService.refundPayment(orderId);
}
}
}
Цей простий приклад ілюструє базовий процес реалізації Saga: виклик сервісів, обробка помилок і виконання компенсаційних дій.
Бонус: реалізацію можна значно спростити і систематизувати з використанням Spring State Machine, Axon Framework або інших інструментів.
З цим фундаментом ти вже готовий рухатися далі — до обговорення оркестрації і хореографії Saga, які дозволяють ще простіше й елегантніше організовувати складні бізнес-процеси.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ