Cy Logic Insights

Распределенные транзакции и целостность данных в архитектуре микросервисов

Рассматриваем двухфазный коммит и паттерн Outbox для обеспечения целостности данных в финансовом программном обеспечении при переходе на микросервисную архитектуру.

Распределенные транзакции и целостность данных в архитектуре микросервисов

Введение и архитектурная трансформация

Корпоративные структуры переходят от монолитных систем к подходу микросервисной архитектуры, чтобы сделать бизнес-процессы масштабируемыми. В частности, разделение критически важных модулей, таких как Финансовые и бухгалтерские системы, обеспечивает изоляцию сбоев, но при этом порождает новые технические сложности в контексте распределенных транзакций (distributed transactions).

Основные проблемы распределенных транзакций

Наличие собственной базы данных у каждого сервиса в рамках микросервисной архитектуры препятствует работе традиционных принципов ACID в границах единой транзакции. При выдаче платежного поручения необходимо одновременно обновить главный бухгалтерский учет и уменьшить баланс соответствующего текущего счета. Координация этих процессов между различными сервисами делает систему уязвимой к нарушениям целостности данных.

Протокол двухфазного коммита (2PC)

Одним из классических методов, используемых для сохранения атомарности в распределенных системах, является механизм двухфазного коммита. Этот протокол состоит из двух фаз:

  • Фаза подготовки (Prepare Phase): Сервис-координатор запрашивает у всех сервисов-участников, может ли транзакция быть завершена. Участники блокируют ресурсы и сообщают о своей готовности.
  • Фаза фиксации (Commit Phase): Если от всех сервисов получено подтверждение, транзакция фиксируется окончательно; в противном случае все изменения откатываются (rollback).

Недостатки 2PC

Финансовое программное обеспечение требует высокой степени параллелизма. Протокол 2PC может приводить к узким местам в производительности всей системы из-за времени блокировки (locking) и сетевых задержек. В случае сбоя сервиса-координатора сохранение заблокированного состояния сервисов-участников создает серьезный фактор риска.

Асинхронный подход с паттерном Outbox

Паттерн Outbox — это асинхронное решение, разработанное для устранения затрат на блокировку, которые вносит протокол двухфазного коммитмента. В этой модели основная операция базы данных и событие, отправляемое наружу, записываются в таблицу outbox в рамках одной локальной транзакции.

Этапы работы Outbox

  1. Локальная транзакция: Сервис применяет бизнес-правило в собственной базе данных и одновременно добавляет запись сообщения в таблицу outbox в рамках той же транзакции.
  2. Передача события: Отдельный процесс (фоновый сканер или инструмент CDC — Change Data Capture) считывает непрочитанные записи из таблицы outbox и передает их в очередь сообщений (Message Broker).
  3. Очистка: Успешно переданные сообщения удаляются или отмечаются в таблице outbox.

Выбор правильного подхода

При проектировании распределенных транзакций необходимо тщательно анализировать бизнес-требования системы. В узких областях, требующих синхронизации в реальном времени, рассматриваются предписывающие протоколы, тогда как в проектах финансового программного обеспечения, где производительность и масштабируемость стоят на первом месте, более безопасной альтернативой выступает паттерн outbox, обеспечивающий конечную согласованность (eventual consistency).

Пример гипотетической работы: Поток платежей в электронной коммерции

В нашем сценарии предположим, что клиент совершает платеж через модуль заказа, интегрированный в инфраструктуру Финансовые и бухгалтерские системы. После создания заказа служба заказов отправляет запрос в службу платежей, а бухгалтерская служба обрабатывает запись о доходе. В этом процессе используется паттерн Outbox для предотвращения блокировок базы данных. Пока служба заказов записывает данные в таблицу заказов, она одновременно добавляет событие «ЗаказСоздан» в таблицу Outbox в том же блоке транзакций. Работающий в фоновом режиме механизм Change Data Capture (CDC) постоянно сканирует эту таблицу и передает новые записи в очередь сообщений вроде Kafka или RabbitMQ. Служба платежей потребляет это сообщение и выполняет операцию списания средств со счета. Если служба платежей временно становится недоступной, данные, ожидающие в очереди сообщений, не блокируют систему; вместо этого механизм очередей запускает политики повторных попыток (retry). Таким образом, система приобретает устойчивость к внезапным сетевым сбоям или простоям сервисов, а благодаря принципу конечной согласованности все сервисы получают доступ к актуальной версии данных синхронным образом. Хотя эта архитектура увеличивает операционную нагрузку, она оптимизирует время отклика системы даже при высоком трафике.

Подробный контрольный список реализации и управление ошибками

Для создания стабильной структуры в распределенных архитектурах обязательно полное выполнение определенных инженерных шагов. Во-первых, в операциях с базами данных обязательно должен быть спроектирован механизм Idempotency (идемпотентность). Для защиты от вероятности многократного поступления одного и того же события из очередей сообщений каждую обрабатываемую операцию необходимо контролировать с помощью уникального ключа. во-вторых, безопасное удаление или пометка прочитанных из таблицы Outbox сообщений как «обработанных» после их передачи в очередь предотвращает дублирование данных. На этапе управления ошибками должна быть создана структура Dead Letter Queue (DLQ); сообщения, потерпевшие неудачу при последовательных попытках, должны собираться здесь и отправляться на ручной анализ. Системные администраторы должны отслеживать в режиме реального времени задержки и размеры очередей в этих процессах, критически важных для финансового программного обеспечения. Кроме того, следует оценить применимость паттерна SAGA для компенсационных транзакций (compensating transactions) и интегрировать библиотеки автоматического выключателя (circuit breaker) для сценариев сетевых разделений (network partition) и таймаутов. Данный контрольный список сводит к минимуму риск потери данных во время распределенных транзакций.

Источники