Распределённые системы
Распределённые транзакции
Цели урока
- Понимать проблему атомарности поперёк независимых сервисов и 4 сценария частичного сбоя
- Знать протокол 2PC, его гарантии и ключевые ограничения (blocking, coordinator failure)
- Применять Saga Pattern с компенсирующими транзакциями для eventual consistency
- Выбирать между Orchestration и Choreography исходя из сложности flow и требований
Предварительные знания
- ACID-транзакции в одной БД (atomicity, isolation, durability)
- Базовое понимание микросервисной архитектуры
- Понятие WAL (Write-Ahead Log) и crash recovery
- Eventual consistency vs strong consistency
eBay в 2004 году терял 1.5 миллиарда долларов в год на частичных сбоях между сервисами - платёж прошёл, заказ не создан. Это не баг в коде, это фундаментальная проблема распределённых систем без правильного протокола.
- **Uber Payments** - Saga с Orchestration для ride payment: 5 шагов (auth, hold, trip, release, charge), compensate при любом сбое
- **Amazon Order Pipeline** - Choreography через EventBridge: 100+ шагов, каждый сервис автономен, нет единого coordinator-а
- **Stripe Payments** - 2PC для internal DB + idempotency keys для API - клиент может безопасно ретраить любой запрос
- **Google Spanner** - distributed transactions через 2PC поверх Paxos: coordinator - лидер группы Paxos
- **Airbnb Booking** - Saga + Outbox Pattern: событие пишется в ту же БД что и бизнес-данные - атомарность через одну транзакцию
2PC и история распределённых транзакций
Two-Phase Commit изобрёл Джим Грей в конце 1970-х в IBM Research. В 1979 году он опубликовал формальное описание в статье 'Notes on Database Operating Systems'. Грей также ввёл понятия ACID, WAL и concurrency control - фундамент всех современных БД. Turing Award 1998 году. В 2007 году Грей исчез в море на своей яхте - тело так и не нашли.
Проблема атомарности поперёк узлов
**2004 год, eBay. Платёжная система отправила списание на PayPal - подтверждение пришло. Но запись о заказе в БД eBay не сохранилась: сервер упал через 80 мс. Покупатель заплатил, товар не зарезервирован. За год такие частичные сбои стоили компании 1.5 миллиарда долларов потерь и возвратов.** Это и есть проблема распределённых транзакций: операция затрагивает несколько узлов, а ACID гарантии действуют только в рамках одного.
**Три сценария частичного сбоя:** 1. первый узел выполнил операцию, второй упал - данные потеряны; (2) оба выполнили, но подтверждение потеряно в сети - операция дублируется при retry; (3) первый выполнил, второй отказал по бизнес-логике - нужен откат первого. Каждый сценарий требует отдельной стратегии.
| Сценарий | Проблема | Нужна стратегия |
|---|---|---|
| Узел B упал после успеха A | Деньги списаны, не зачислены | 2PC rollback или компенсация |
| Ответ A потерялся в сети | Клиент делает retry - двойное списание | Idempotency key |
| B отказал по бизнес-логике | A выполнено, B нет - нужен откат A | Saga compensating transaction |
| Coordinator упал в середине | Участники заблокированы в PREPARED | 2PC timeout + 3PC или Saga |
Транзакция в микросервисах работает так же, как BEGIN/COMMIT в PostgreSQL
Каждый сервис фиксирует свою локальную транзакцию независимо - нет общего ROLLBACK поперёк границ
PostgreSQL ACID - это один process, один WAL, один lock manager. В распределённой системе у каждого сервиса своя БД, своя изоляция, свой момент commit. Координировать их можно только через протоколы (2PC) или паттерны (Saga) - и ни тот ни другой не даёт полного ACID.
Клиент вызвал перевод денег: сервис A (списание) ответил OK, сервис B (зачисление) упал. Какое состояние системы?
Two-Phase Commit (2PC)
**Two-Phase Commit** - классический протокол для атомарных распределённых транзакций. Один узел становится Coordinator, остальные - Participants. Протокол строится на блокировке ресурсов до момента финального решения: либо все коммитят, либо все откатывают.
| Проблема 2PC | Описание | Последствие |
|---|---|---|
| Blocking | Ресурсы залочены от Phase 1 до Phase 2 | При высоком RPS - деградация throughput на 5-10x |
| Coordinator failure | Участники зависают в PREPARED бесконечно | Deadlock до восстановления coordinator-а |
| Network partition | Coordinator не получает ответы от части участников | Timeout - принудительный ABORT даже если все готовы |
| Не масштабируется | Synchronous wait всех участников | Latency = max(latency всех узлов) |
**2PC - blocking protocol.** Если Coordinator упал после Phase 1 (все ответили YES) но до Phase 2 - участники держат локи неопределённо долго. Эту проблему частично решает Three-Phase Commit (3PC), но у него свои trade-offs. В production с микросервисами 2PC практически не применяется.
Coordinator в 2PC записал COMMIT в свой WAL, но упал до отправки COMMIT участникам. Что произойдёт?
Saga Pattern: цепочка компенсаций
**Uber, 2016 год. При запуске микросервисной архитектуры столкнулись с проблемой: 2PC не работает поперёк 50+ независимых сервисов (trips, payments, drivers, surge, promotions). Решение - Saga Pattern: длинная транзакция разбивается на цепочку локальных транзакций. Каждая шаг имеет компенсирующее действие (compensating transaction). Если шаг N упал - выполняются компенсации шагов N-1, N-2, ... в обратном порядке.**
**Ключевое отличие компенсации от отката:** обычный ROLLBACK стирает как будто операции не было. Компенсирующая транзакция - это новая бизнес-операция (возврат платежа, снятие резерва). Она видна в логах, она может частично не работать, она должна быть idempotent. Это eventual consistency, а не ACID.
Saga гарантирует ACID так же как локальная транзакция
Saga даёт eventual consistency - между шагами система находится в промежуточном состоянии
После шага 1 (списание) и до шага 2 (зачисление) проходит реальное время - десятки мс. Другие клиенты могут видеть списанные деньги и незачисленный товар. Это называется ACI без D в терминах BASE (Basic Availability, Soft state, Eventual consistency). Для большинства бизнес-процессов это приемлемо.
В Saga шаги 1 и 2 выполнены успешно, шаг 3 упал. Что должно произойти?
Choreography vs Orchestration
**Saga реализуется двумя способами.** Orchestration: один центральный сервис (Orchestrator) вызывает все шаги по порядку и управляет компенсациями. Choreography: каждый сервис слушает события и реагирует самостоятельно - нет центрального координатора. Amazon Order Service, Uber Dispatch, Netflix Watch Party - все используют гибрид в зависимости от сложности flow.
| Подход | Как работает | Плюсы | Минусы |
|---|---|---|---|
| Orchestration | Центральный сервис вызывает шаги и компенсации | Flow читается как код, легко debug, rollback явный | Single point of failure, coupling через orchestrator |
| Choreography | Сервисы реагируют на события из очереди | Loose coupling, нет SPOF, легко добавить шаг | Flow неявный, сложно debug, compensate надо продумывать |
**Правило выбора:** если flow линейный и 3-5 шагов - Orchestration (проще debug). Если шагов 10+ или нужна максимальная автономность сервисов - Choreography. Uber использует Orchestration для критических финансовых flow (ride payment) и Choreography для аналитических pipeline.
| Система | Подход | Почему |
|---|---|---|
| Uber Payments | Orchestration | Финансовая атомарность, нужен явный audit trail |
| Amazon Order | Choreography | 100+ шагов, независимые команды, loose coupling |
| Netflix Billing | Orchestration | Compliance требует предсказуемого rollback |
| Airbnb Booking | Saga + Outbox | Outbox pattern для надёжной публикации событий |
Какой подход лучше для финансового процесса с 4 шагами, где важен чёткий audit log и предсказуемый rollback?
Вопросы для размышления
- Банк реализует перевод между счетами через Saga (списание с одного счёта - зачисление на другой). Между шагами 1 и 2 проходит 200 мс. Какие проблемы могут возникнуть и как их решить?
Связанные уроки
- ds-03-consensus — Atomic commit requires agreement across nodes
- dist-07-2pc — 2PC is the standard distributed transaction commit protocol
- dist-12-consistency — Transaction isolation level determines consistency guarantees
- ds-11-distributed-locks — Pessimistic transactions use distributed locks
- db-13-transactions