Распределённые транзакции и Saga Pattern
Зачем знать: В мире микросервисов каждая команда имеет свою БД. Один бизнес-процесс (заказ, перевод денег, бронирование) часто затрагивает несколько сервисов. Локальной ACID-транзакции уже мало — нужны распределённые механизмы координации. На уровне Middle 2 ты обязан понимать ограничения 2PC, идиомы Saga, eventual consistency, и уметь спроектировать процесс заказа, где failures возможны на любом шаге.
Содержание
Заголовок раздела «Содержание»- Концепция: зачем distributed transactions
- Под капотом: 2PC, TCC, Saga
- Gotchas
- Production-практики
- Вопросы для собеседования
- Practice
- Источники
1. Концепция: зачем distributed transactions
Заголовок раздела «1. Концепция: зачем distributed transactions»1.1 Проблема
Заголовок раздела «1.1 Проблема»Монолит → одна БД → одна транзакция → ACID — всё прекрасно.
BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id = A; UPDATE accounts SET balance = balance + 100 WHERE id = B;COMMIT;Микросервисы → разные БД (или одна, но логически разделённые домены).
[Order Service] → [Payment Service] → [Inventory Service] → [Notification Service] orders_db payments_db inventory_db notifications_dbЕсли на 3-м шаге упало — что делать с уже созданным заказом и списанным платежом?
1.2 ACID vs BASE
Заголовок раздела «1.2 ACID vs BASE»ACID (классические транзакции):
- Atomicity — всё или ничего.
- Consistency — данные не нарушают invariants.
- Isolation — параллельные транзакции не мешают друг другу.
- Durability — после COMMIT данные сохранены.
BASE (распределённые системы):
- Basically Available — система остаётся доступной (предпочтение availability над consistency).
- Soft state — состояние может меняться даже без новых вводов (репликация).
- Eventual consistency — рано или поздно все узлы сойдутся к одному состоянию.
CAP теорема (Brewer):
- Consistency
- Availability
- Partition tolerance
Можно выбрать только 2 из 3 в случае network partition. В реальности P неизбежен → выбор между CP и AP.
- CP систем: MongoDB (с majority writes), HBase, Zookeeper, etcd, CockroachDB.
- AP систем: Cassandra, DynamoDB (в default mode), CouchDB.
1.3 Когда нужны distributed transactions
Заголовок раздела «1.3 Когда нужны distributed transactions»- Money transfers (банки): нельзя списать и не зачислить.
- Order processing (e-commerce): зарезервировать → оплатить → отправить.
- Inventory + Reservation.
Когда НЕ нужны:
- Если можно сложить эти данные в одну БД — лучше так.
- Если операция идемпотентна и можно retry — eventual consistency через очереди.
- Если можно сделать одну “владеющую” сервис, а остальные — read-only consumers.
1.4 Спектр решений
Заголовок раздела «1.4 Спектр решений»Strong consistency ←————————————————————————————→ Eventual consistency
2PC / XA → 3PC → TCC → Saga (orch.) → Saga (chor.) → Pure events[blocking] [less] [comp.] [explicit] [implicit] [no compensation]2. Под капотом / Архитектура
Заголовок раздела «2. Под капотом / Архитектура»2.1 Two-Phase Commit (2PC)
Заголовок раздела «2.1 Two-Phase Commit (2PC)»Идея: координатор (transaction manager) проводит транзакцию через 2 фазы.
Coordinator Participant A Participant B │ │ │ │── Prepare ────────────────────────►│ │ │ │ (lock, write WAL, │ │ return YES) │ │◄────────── YES ───────────────────── │ │── Prepare ─────────────────────────────────────────►│ │ │ (YES) │◄────────── YES ─────────────────────────────────────│ │ │ │ │── Commit ─────────────────────────►│ │ │ │ (apply, release locks) │── Commit ─────────────────────────────────────────►│ │ │Фаза 1 (Prepare):
- Каждый участник проверяет, может ли он commit.
- Если YES — записывает в durable log “готов к commit” и блокирует ресурсы.
- Если NO — голосует против.
Фаза 2 (Commit/Abort):
- Если все YES → координатор шлёт COMMIT.
- Если хоть один NO → координатор шлёт ABORT.
- Участники применяют решение и освобождают locks.
XA standard (X/Open) — формализация 2PC. Поддерживается Oracle, PostgreSQL (prepared transactions), MySQL (XA transactions).
-- PostgreSQL XA примерBEGIN;-- ... операцииPREPARE TRANSACTION 'tx_123';-- координатор где-то решаетCOMMIT PREPARED 'tx_123';-- илиROLLBACK PREPARED 'tx_123';Проблема 1: Coordinator failure
- Если координатор упал после Prepare, но до Commit — участники зависли с locks.
- Это blocking protocol — участники ждут координатора.
- Для recovery нужен durable coordinator log.
Проблема 2: Performance
- 2 round-trip (Prepare + Commit) к каждому участнику.
- Locks держатся долго.
- Не масштабируется (10+ участников — медленно и хрупко).
Где используется в 2026:
- Внутри транзакционных систем (Oracle RAC, single-DB clustering).
- В legacy enterprise (JTA в Java EE).
- В микросервисах — почти не используется из-за blocking и tight coupling.
2.2 Three-Phase Commit (3PC)
Заголовок раздела «2.2 Three-Phase Commit (3PC)»Идея: добавить дополнительную фазу PreCommit, чтобы избежать blocking.
Phase 1: CanCommit? (как Prepare в 2PC, но без блокировки)Phase 2: PreCommit (все согласились — все знают, что будет commit)Phase 3: DoCommit (применить)Преимущество: если координатор упал в Phase 3, участники видят PreCommit и могут сами завершить.
Недостатки:
- Дополнительный round-trip.
- Не работает корректно при network partitions (Skeen’s theorem).
- Реально почти не используется.
2.3 TCC (Try-Confirm-Cancel)
Заголовок раздела «2.3 TCC (Try-Confirm-Cancel)»Идея: каждый сервис предоставляет 3 операции:
- Try — резервирует ресурс, не финализирует.
- Confirm — финализирует.
- Cancel — отменяет резерв.
[Coordinator] │ ├─► Inventory.Try(reserve 5 items) ── success ├─► Payment.Try(hold $100) ── success │ ├─► Inventory.Confirm() (или Cancel при failure) ├─► Payment.Confirm()Сравнение с 2PC:
- Не требует XA — обычные HTTP/gRPC вызовы.
- Бизнес-логика владеет Try/Confirm/Cancel — больше контроля.
- Каждый сервис реализует idempotent Confirm/Cancel.
Примеры:
- Inventory.Try: создать “reservation” record (TTL 10 min).
- Inventory.Confirm: снять с stock.
- Inventory.Cancel: удалить reservation.
Используется в финтехе (Alipay, Bank of China).
2.4 Saga Pattern
Заголовок раздела «2.4 Saga Pattern»Идея (Garcia-Molina, Salem, 1987): длинная транзакция как последовательность локальных транзакций + компенсирующие транзакции для отмены.
Saga = [T1, T2, T3, T4]Compensations = [C1, C2, C3, C4]
Если T3 упал: C2; C1; ← компенсации в обратном порядке
Если T4 упал: C3; C2; C1;Каждое Ti — локальная ACID-транзакция в своём сервисе. Каждое Ci — обратное действие (refund вместо charge).
Пример: Order processing
T1: Order Service — создать заказ (status=pending)T2: Inventory Service — зарезервировать товарыT3: Payment Service — списать деньгиT4: Shipping Service — создать shipmentT5: Notification Service — отправить email
C5: Notification — (nothing, email не отменить)C4: Shipping — отменить shipmentC3: Payment — refundC2: Inventory — освободить резервC1: Order — установить status=cancelled2.5 Saga: Orchestration vs Choreography
Заголовок раздела «2.5 Saga: Orchestration vs Choreography»Orchestration (с координатором)
Заголовок раздела «Orchestration (с координатором)»Центральный сервис (orchestrator) дирижирует всем процессом.
┌─────────────────────────┐ │ Saga Orchestrator │ └────────────┬────────────┘ │ ┌─────────────────┼──────────────────┐ ▼ ▼ ▼[Inventory] [Payment] [Shipping]Плюсы:
- Явная логика, легко понять.
- Централизованное логирование и monitoring.
- Легче тестировать.
Минусы:
- Точка отказа (нужен HA orchestrator).
- Tighter coupling — orchestrator знает все сервисы.
Tools (2026):
- Temporal (бывший Cadence от Uber) — самый популярный workflow engine.
- Camunda Zeebe / Camunda 8 — BPMN-based.
- Conductor (Netflix).
- Airflow — для batch workflows (не realtime).
Choreography (без координатора)
Заголовок раздела «Choreography (без координатора)»Сервисы шлют events, каждый реагирует на нужные.
[Order Service] —OrderCreated→ [Kafka] ↓[Inventory] reads OrderCreated → reserves → InventoryReserved → [Kafka] ↓[Payment] reads InventoryReserved → charges → PaymentCompleted → [Kafka] ↓[Shipping] reads PaymentCompleted → ships → ShipmentCreated → [Kafka]Плюсы:
- Loose coupling.
- Сервисы независимы.
- Хорошо масштабируется.
Минусы:
- Логика размазана по сервисам — трудно отследить процесс.
- Нет single source of truth о состоянии saga.
- Debugging сложнее (нужен distributed tracing).
Tools:
- Kafka / NATS / RabbitMQ как event bus.
- Schema registry для events.
2.6 Saga в Go (Temporal example)
Заголовок раздела «2.6 Saga в Go (Temporal example)»Temporal — самый популярный workflow engine с Go SDK.
// Workflow definitionfunc OrderWorkflow(ctx workflow.Context, order Order) error { ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ StartToCloseTimeout: 30 * time.Second, RetryPolicy: &temporal.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2.0, MaximumAttempts: 3, }, })
// Step 1: Reserve inventory var reservationID string if err := workflow.ExecuteActivity(ctx, ReserveInventory, order).Get(ctx, &reservationID); err != nil { return err // Никаких компенсаций — ничего не сделано }
// Step 2: Charge payment var paymentID string if err := workflow.ExecuteActivity(ctx, ChargePayment, order).Get(ctx, &paymentID); err != nil { // Compensate workflow.ExecuteActivity(ctx, ReleaseInventory, reservationID).Get(ctx, nil) return err }
// Step 3: Create shipment var shipmentID string if err := workflow.ExecuteActivity(ctx, CreateShipment, order).Get(ctx, &shipmentID); err != nil { // Compensate in reverse workflow.ExecuteActivity(ctx, RefundPayment, paymentID).Get(ctx, nil) workflow.ExecuteActivity(ctx, ReleaseInventory, reservationID).Get(ctx, nil) return err }
// Step 4: Notify (best-effort) workflow.ExecuteActivity(ctx, SendOrderConfirmation, order).Get(ctx, nil)
return nil}
// Activityfunc ReserveInventory(ctx context.Context, order Order) (string, error) { // Locally transactional call to inventory service resp, err := inventoryClient.Reserve(ctx, &inventory.ReserveRequest{ OrderID: order.ID, Items: order.Items, }) if err != nil { return "", err } return resp.ReservationID, nil}Что Temporal даёт:
- Durability: workflow state хранится в Temporal DB. Если worker упал — другой подхватит и продолжит.
- Retries: автоматические с backoff.
- Timeouts: на каждой activity.
- Compensations через явные branches.
- Visibility: UI для просмотра всех workflows.
- Versioning: возможность менять workflow code без поломки in-flight.
2.7 Eventual Consistency
Заголовок раздела «2.7 Eventual Consistency»Определение: после прекращения новых обновлений, рано или поздно все реплики сойдутся к одному состоянию.
Window of inconsistency — время между событием и его propagation на все узлы.
Примеры:
- DNS — обновление IP видно везде через TTL.
- Cassandra —
eventual consistency(можно настроить QUORUM для CP). - Saga — пока saga не завершилась, частичное состояние видно.
Принципы работы с EC:
- Идемпотентность операций.
- Дедупликация сообщений.
- Read-after-write challenges (часто читай с master).
- UI: оптимистичные updates (“loading…”, retry на ошибку).
2.8 Idempotency
Заголовок раздела «2.8 Idempotency»Определение: операция, повторяющаяся с тем же input, даёт тот же результат и не имеет побочных эффектов.
Зачем критично в saga:
- Сеть теряет ответы — клиент повторяет.
- Worker упал в середине — другой повторяет step.
- Без идемпотентности → дубли (двойная оплата, двойная отправка).
Реализация:
Подход 1: Idempotency Key
Заголовок раздела «Подход 1: Idempotency Key»type ChargeRequest struct { IdempotencyKey string // UUID, генерируется клиентом Amount int64 AccountID int64}
func (s *PaymentService) Charge(ctx context.Context, req ChargeRequest) (*Charge, error) { // Check if this key was processed existing, err := s.repo.FindByIdempotencyKey(ctx, req.IdempotencyKey) if err == nil { return existing, nil // Return cached result }
// Process and save charge := &Charge{ID: uuid.New(), IdempotencyKey: req.IdempotencyKey, ...} if err := s.repo.Save(ctx, charge); err != nil { return nil, err } return charge, nil}В БД:
CREATE UNIQUE INDEX idx_charges_idem ON charges(idempotency_key);При повторном INSERT — ON CONFLICT DO NOTHING или возврат существующего.
Подход 2: State machine
Заголовок раздела «Подход 2: State machine»Order: pending → paid → shipped → deliveredЕсли в pending уже — Charge сделает paid.
Если уже paid — Charge no-op (idempotent).
Подход 3: At-most-once через external lock
Заголовок раздела «Подход 3: At-most-once через external lock»Менее предпочтительный, но иногда нужен (внешние API без idempotency).
2.9 Saga: классический пример Order
Заголовок раздела «2.9 Saga: классический пример Order»1. Order Service ──CreateOrder──► Order(status=pending) created2. Inventory Service ──Reserve────► Items reserved (TTL 30 min)3. Payment Service ──Charge─────► Money charged4. Order Service ──Confirm────► Order(status=paid)5. Shipping Service ──Ship───────► Shipment created6. Order Service ──Update─────► Order(status=shipped)7. Notification ──Email──────► Confirmation sent
Failure at step 5 (Shipping fails):4'. Payment Service ──Refund─────► Money refunded3'. Inventory Service──Release────► Items released2'. Order Service ──Cancel─────► Order(status=cancelled)Что компенсировать нельзя:
- Email уже отправлен → нельзя отозвать, только отправить compensating email “извините, отменено”.
- Внешний платёж через bank wire → требуется ручная компенсация.
- Physical действия (отправили посылку, нельзя её “не отправить”) — нужна страховка.
2.10 Tradeoffs: Saga vs 2PC
Заголовок раздела «2.10 Tradeoffs: Saga vs 2PC»| Аспект | 2PC | Saga |
|---|---|---|
| Consistency | Strong | Eventual |
| Coupling | Tight (XA across DBs) | Loose (через events) |
| Performance | Низкая (locks) | Высокая (no locks) |
| Failure handling | Blocking | Compensation |
| Complexity | Меньше кода (DB делает) | Больше кода (компенсации) |
| Микросервисы | Не подходит | Подходит |
| Идемпотентность | Не требует | Требует |
| Видимость промежуточных состояний | Нет | Да (другие транзакции видят) |
| Применение | Single org, controlled env | Distributed, microservices |
3. Gotchas
Заголовок раздела «3. Gotchas»3.1 ⚠️ Distributed transactions — не серебряная пуля
Заголовок раздела «3.1 ⚠️ Distributed transactions — не серебряная пуля»Перед distributed tx подумайте: можно ли:
- Объединить сервисы (если связь сильная)?
- Использовать eventual consistency через events?
- Сделать один сервис “source of truth”?
Микросервисы с distributed tx часто = distributed monolith (худшее обоих миров).
3.2 ⚠️ Saga не = транзакция
Заголовок раздела «3.2 ⚠️ Saga не = транзакция»В saga нет isolation. Другие операции видят промежуточные состояния:
- Между Inventory.Reserve и Payment.Charge — товар “заблокирован”, но не оплачен.
- Если другой пользователь хочет купить — видит “out of stock”.
Это бизнес-проблема, а не bug.
3.3 ⚠️ Компенсация ≠ undo
Заголовок раздела «3.3 ⚠️ Компенсация ≠ undo»Компенсация — это новое действие, отменяющее эффект.
- Refund — не “отмена charge”, а новая transaction.
- Cancel email — отправка следующего email.
Аудит-логи будут содержать обе записи (charge + refund).
3.4 ⚠️ Order of operations matters
Заголовок раздела «3.4 ⚠️ Order of operations matters»T1: Create userT2: Send welcome emailЕсли T2 упал → C1 (delete user). Но email уже отправлен → пользователь получил “welcome”, аккаунт удалён.
Решение: переставить — сначала email, потом user. Или: email — best-effort (без компенсации, просто log).
3.5 ⚠️ Compensation может тоже упасть
Заголовок раздела «3.5 ⚠️ Compensation может тоже упасть»T3 failed → run C2 → C2 also fails → ???Что делать?
- Retry C2 с backoff (обычно).
- Если retry не помогает — alert человеку.
- Dead Letter Queue + manual intervention.
- В Temporal — это решается через retry policies + alerts.
3.6 ⚠️ Идемпотентность сложнее, чем кажется
Заголовок раздела «3.6 ⚠️ Идемпотентность сложнее, чем кажется»Не только “не дублировать”. Нужно учитывать:
- Race conditions (одновременные retries).
- Side effects (отправка emails — не идемпотентна по умолчанию).
- External API без idempotency key.
3.7 ⚠️ Temporal — не панацея
Заголовок раздела «3.7 ⚠️ Temporal — не панацея»Плюсы:
- Durability, retries.
- Visibility.
Минусы:
- Сложный для понимания (workflows ≠ обычный код, есть restrictions).
- Дополнительная инфра (Temporal cluster, БД).
- Lock-in (workflow code привязан к Temporal SDK).
- Versioning workflows — отдельная сложная тема.
3.8 ⚠️ Choreography → spaghetti
Заголовок раздела «3.8 ⚠️ Choreography → spaghetti»Если 10+ сервисов слушают events друг друга — никто не знает полную картину.
Service A → event X → Service B reads X, emits Y → Service C reads X, emits ZService D reads Y AND Z, emits WService E reads W, emits...Решение:
- Event catalog (документация events).
- Distributed tracing (Jaeger/Tempo).
- Limited number of “core” events.
3.9 ⚠️ XA в managed DB
Заголовок раздела «3.9 ⚠️ XA в managed DB»AWS RDS, Cloud SQL — частично поддерживают XA, но это медленно. Часто XA отключают для performance.
3.10 ⚠️ Sagas не для финансовых транзакций (в чистом виде)
Заголовок раздела «3.10 ⚠️ Sagas не для финансовых транзакций (в чистом виде)»Финансы требуют strong consistency. Saga работает, если:
- Каждый шаг — local ACID.
- Есть escrow (промежуточный счёт) для money.
- Регуляторное требование — audit trail (Saga его естественно даёт).
В классических банках — двойная запись (debit + credit) одной транзакцией.
3.11 ⚠️ Timeouts в saga
Заголовок раздела «3.11 ⚠️ Timeouts в saga»Каждый шаг должен иметь timeout. Иначе:
- Inventory.Reserve висит 5 минут.
- Клиент думает, что заказ не прошёл.
- Дублирующий заказ.
Решение: explicit timeouts + cancellation propagation.
3.12 ⚠️ Saga + parallel steps
Заголовок раздела «3.12 ⚠️ Saga + parallel steps»Не все шаги — sequential. Можно делать parallel:
T1: Create order↓T2, T3 parallel: Reserve inventory, Verify customer↓T4: Charge↓T5: ShipКомпенсация parallel branches — сложнее. Temporal поддерживает через workflow.Go() и channels.
3.13 ⚠️ Saga и database constraints
Заголовок раздела «3.13 ⚠️ Saga и database constraints»Локальные транзакции в saga могут нарушать глобальные invariants.
Пример: invariant “total inventory ≥ 0”.
- T1 ─ Reserve 10 items (inventory: 5 → -5)
- Если бизнес запрещает minus — Reserve вернёт ошибку (compensation chain).
Но если physical stock = 5 (другие резервы есть), решение принимается на уровне Inventory service логики.
4. Production-практики
Заголовок раздела «4. Production-практики»4.1 Выбор: 2PC vs Saga vs Eventual Consistency
Заголовок раздела «4.1 Выбор: 2PC vs Saga vs Eventual Consistency»Decision tree:
- Все сервисы на одной БД (или поддерживают XA)? И acceptable latency 100ms+? → 2PC (просто).
- Микросервисы с разными БД? Compensation возможна? → Saga.
- Compensation невозможна или сложна? Можно жить с eventual consistency? → Events + Idempotent consumers.
- Multi-step + complex orchestration? → Temporal/Cadence.
4.2 Idempotency Key endpoint pattern
Заголовок раздела «4.2 Idempotency Key endpoint pattern»Standard pattern для REST API (а-ля Stripe):
POST /v1/chargesIdempotency-Key: 7f8d9e0a-1234-...{ "amount": 1000, "currency": "USD"}В Go:
type Handler struct { cache redis.Client repo Repo}
func (h *Handler) Charge(c *gin.Context) { key := c.GetHeader("Idempotency-Key") if key == "" { c.JSON(400, gin.H{"error": "missing Idempotency-Key"}) return }
// Check cache if cached := h.cache.Get(ctx, "idem:"+key); cached != nil { c.JSON(200, cached) return }
// Process result, err := h.process(...)
// Cache result (with TTL 24h) h.cache.Set(ctx, "idem:"+key, result, 24*time.Hour)
c.JSON(200, result)}4.3 Outbox pattern для saga steps
Заголовок раздела «4.3 Outbox pattern для saga steps»(Подробнее в файле 18.)
Чтобы избежать dual-write (DB + queue):
BEGIN; UPDATE orders SET status = 'reserved' WHERE id = $1; INSERT INTO outbox (event_type, payload) VALUES ('OrderReserved', $2);COMMIT;Worker читает outbox → публикует event → mark sent.
4.4 Temporal worker setup
Заголовок раздела «4.4 Temporal worker setup»func main() { c, err := client.Dial(client.Options{ HostPort: "temporal.local:7233", Namespace: "default", }) if err != nil { log.Fatalln("Unable to create client", err) } defer c.Close()
w := worker.New(c, "order-task-queue", worker.Options{}) w.RegisterWorkflow(OrderWorkflow) w.RegisterActivity(ReserveInventory) w.RegisterActivity(ChargePayment) w.RegisterActivity(CreateShipment) w.RegisterActivity(SendOrderConfirmation)
if err := w.Run(worker.InterruptCh()); err != nil { log.Fatalln("Unable to start worker", err) }}4.5 Monitoring saga
Заголовок раздела «4.5 Monitoring saga»Метрики:
- Saga started / completed / failed (rate, по типам).
- Saga duration (histogram).
- Step duration (per step).
- Compensation rate (по step).
- DLQ size.
Алерты:
- Saga failure rate > 1%.
- Saga stuck > 10 минут (timeout).
- Compensation failed.
- DLQ растёт.
4.6 Saga testing
Заголовок раздела «4.6 Saga testing»Unit tests для activities: обычные тесты.
Workflow tests (Temporal):
func TestOrderWorkflow(t *testing.T) { s := &testsuite.WorkflowTestSuite{} env := s.NewTestWorkflowEnvironment()
env.OnActivity(ReserveInventory, mock.Anything, mock.Anything).Return("res-123", nil) env.OnActivity(ChargePayment, mock.Anything, mock.Anything).Return("pay-456", errors.New("payment failed")) env.OnActivity(ReleaseInventory, mock.Anything, "res-123").Return(nil)
env.ExecuteWorkflow(OrderWorkflow, Order{ID: "1"})
require.True(t, env.IsWorkflowCompleted()) require.Error(t, env.GetWorkflowError())}Integration tests: запустить Temporal docker + sample services + проверить full flow.
Chaos testing: симулировать failures на каждом шаге, убедиться, что компенсация корректна.
4.7 Versioning sagas
Заголовок раздела «4.7 Versioning sagas»Saga может выполняться часами/днями. Что если деплоится новая версия?
Temporal:
workflow.GetVersion(ctx, "v2-shipping-logic", workflow.DefaultVersion, 1)— определяет, какая версия исполняется.- Старые workflows доживают на старой версии.
Choreography:
- Backward-compatible event schemas (Avro, Protobuf).
- Schema registry.
4.8 Distributed tracing
Заголовок раздела «4.8 Distributed tracing»OpenTelemetry — стандарт 2026.
ctx, span := otel.Tracer("payment").Start(ctx, "ChargePayment")defer span.End()
// Trace ID propagates через gRPC/HTTP headers → видно в Jaeger/TempoВ Temporal — встроенная интеграция с OTel.
4.9 Audit logging
Заголовок раздела «4.9 Audit logging»Saga естественно даёт audit log. Сохранять:
- Все steps (success/failure).
- Все compensations.
- Decision points.
Для compliance (finance, healthcare).
4.10 Realistic order processing — production architecture
Заголовок раздела «4.10 Realistic order processing — production architecture»Client → API Gateway → Order Service │ ├─► Outbox table → Debezium → Kafka │ ▼ Temporal Workflow │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ Inventory Payment Shipping Service Service Service (own DB) (own DB) (own DB) │ │ │ └─────────────┼─────────────┘ ▼ Kafka events │ ┌───────┴────────┐ ▼ ▼ Notification Analytics Service pipeline5. Вопросы для собеседования
Заголовок раздела «5. Вопросы для собеседования»-
Чем отличается ACID от BASE? ACID — strong consistency, локальные транзакции. BASE — basically available, soft state, eventual consistency. ACID для одной БД, BASE для распределённых систем.
-
Что такое CAP теорема? В распределённой системе при network partition можно иметь только 2 из 3: Consistency, Availability, Partition tolerance. Реально выбор между CP и AP.
-
Что такое 2PC? Two-Phase Commit. Координатор + участники. Фаза 1: Prepare (lock + vote). Фаза 2: Commit/Abort. Гарантирует atomicity, но blocking при coordinator failure.
-
Почему 2PC плохо для микросервисов? Blocking при failures, медленный (round-trips), tight coupling (XA across DBs), не масштабируется.
-
Что такое XA standard? Спецификация 2PC от X/Open. Поддерживается СУБД и Java EE (JTA).
-
Что такое 3PC? 3-фазный протокол. Добавлена фаза PreCommit для устранения blocking. Не работает корректно при network partitions. Редко используется.
-
Что такое TCC? Try-Confirm-Cancel. Сервис предоставляет 3 операции: Try (reserve), Confirm (apply), Cancel (rollback). Координатор оркестрирует.
-
Что такое Saga pattern? Длинная транзакция как sequence локальных транзакций + компенсирующие транзакции для отката. Используется в микросервисах.
-
Чем orchestration отличается от choreography в saga? Orchestration — центральный orchestrator дирижирует. Choreography — сервисы реагируют на events независимо.
-
Когда orchestration лучше, когда choreography? Orchestration — для сложной логики, visibility, audit. Choreography — для простой логики и максимального decoupling.
-
Что такое eventual consistency? После остановки новых обновлений, рано или поздно все узлы сойдутся к одному состоянию.
-
Что такое идемпотентность и зачем в saga? Операция, повторяющаяся с тем же input, даёт тот же результат без побочных эффектов. Необходима в saga, т.к. retries неизбежны.
-
Как реализовать idempotency? Idempotency-Key в API + проверка уникальности в БД. State machine — переход только из конкретного состояния.
-
Что такое compensating transaction? Действие, отменяющее эффект предыдущей транзакции (например, refund для charge). Не undo, а новая транзакция.
-
Что делать, если compensation тоже падает? Retry с backoff. Если не помогает — alert человеку, DLQ, manual intervention.
-
Какие есть популярные workflow engines в 2026? Temporal (стандарт), Cadence (предшественник Temporal от Uber), Camunda Zeebe (BPMN), Conductor (Netflix).
-
Зачем нужен Temporal? Durability workflows, автоматические retries, timeouts, visibility, compensations. Решает проблему “длинных распределённых процессов”.
-
В чём workflow Temporal отличается от обычного кода? Workflow должен быть deterministic. Нельзя использовать time.Now, rand.Int напрямую — только через workflow API. State хранится в Temporal, при failure worker’a workflow продолжается.
-
Что такое activity в Temporal? Конкретное действие (HTTP-вызов, БД-операция). Не deterministic. Идемпотентно. Имеет retry policy.
-
Какие шаги нельзя компенсировать? Отправленные emails, SMS, physical actions (shipped items). Решения: avoid (best-effort steps в конце), compensating notification (“извините, отменено”), insurance.
-
Что делать, если 5 сервисов слушают одно событие, и порядок важен? Использовать orchestrator (Temporal). Или ввести “core” сервис, который коордирует. Choreography без порядка не подходит.
-
Как тестировать saga? Unit tests для activities. Workflow tests (Temporal test framework — мокать activities). Integration tests на staging. Chaos testing (симулировать failures).
-
Что такое distributed tracing и почему важно для saga? Tracing запроса через все сервисы (OpenTelemetry, Jaeger, Tempo). Критично для debugging — иначе невозможно понять, где упало.
-
Как добавить новый шаг в running saga? Workflow versioning. В Temporal —
workflow.GetVersion(). Старые workflows доживают на старой версии, новые — на новой. -
Saga vs Event-Driven Architecture — в чём разница? Saga — конкретный pattern для consistency. EDA — общая архитектура. Saga может реализовываться поверх EDA (choreography saga).
6. Practice
Заголовок раздела «6. Practice»Задача 1: Реализовать TCC для бронирования отеля
Заголовок раздела «Задача 1: Реализовать TCC для бронирования отеля»3 сервиса: Hotel, Payment, Customer (loyalty points).
- Try: reserve room, hold payment, deduct points temp.
- Confirm: confirm reservation, charge, deduct points permanent.
- Cancel: release room, refund hold, restore points.
Задача 2: Saga с компенсациями на Go (без Temporal)
Заголовок раздела «Задача 2: Saga с компенсациями на Go (без Temporal)»Простой in-memory orchestrator. Steps + compensations как функции. Тест: симулировать failure на step 3, проверить, что C2, C1 выполнились.
Задача 3: Saga на Temporal
Заголовок раздела «Задача 3: Saga на Temporal»Order processing workflow (5 шагов). Запустить Temporal в Docker. Implement activities. Тест на failure scenarios.
Задача 4: Idempotent API endpoint
Заголовок раздела «Задача 4: Idempotent API endpoint»POST /charges с Idempotency-Key header. Дубли возвращают cached result. Использовать Redis для хранения.
Задача 5: Choreography saga
Заголовок раздела «Задача 5: Choreography saga»3 сервиса + Kafka. Order Service → OrderCreated. Inventory → InventoryReserved. Payment → PaymentCompleted. Реализовать reactive consumers.
Задача 6: Сравнить 2PC и Saga (написать essay 1 страница)
Заголовок раздела «Задача 6: Сравнить 2PC и Saga (написать essay 1 страница)»Когда какой выбрать. Trade-offs. Real-world примеры.
Задача 7: Реализовать DLQ для failed compensations
Заголовок раздела «Задача 7: Реализовать DLQ для failed compensations»Если C2 трижды упал — отправить в DLQ + slack alert. Manual replay endpoint.
7. Источники
Заголовок раздела «7. Источники»- Microservices Patterns by Chris Richardson — глава про Saga и обработку distributed transactions.
- Designing Data-Intensive Applications by Martin Kleppmann — главы 7-9: транзакции, distributed systems, consistency.
- Temporal Documentation — https://docs.temporal.io/ (особенно Go SDK + workflow patterns).
- The Saga Pattern by Hector Garcia-Molina, Kenneth Salem (1987) — оригинальная статья.
- Building Event-Driven Microservices by Adam Bellemare — отличная книга про event-driven, EDA, Kafka.
- Microservices.io patterns — https://microservices.io/patterns/data/saga.html
- Pat Helland — “Life Beyond Distributed Transactions” — classic paper.
- Stripe API Docs (Idempotency) — https://stripe.com/docs/api/idempotent_requests — реальный пример индустриальной практики.
- Confluent Blog — https://www.confluent.io/blog/ — паттерны event-driven.
- AWS Step Functions docs — альтернативная workflow платформа.