Cy Logic Insights

Обеспечение целостности данных в системных интеграциях с помощью паттерна Outbox

Как обеспечить целостность данных между публикацией в очередь сообщений и операциями с базой данных в распределенных архитектурах с помощью паттерна Outbox?

Обеспечение целостности данных в системных интеграциях с помощью паттерна Outbox

Целостность данных в распределенных архитектурах и сопутствующие трудности

В современных программных проектах слаженная работа различных сервисов и модулей является критически важным требованием. Особенно в процессах Системных интеграций необходимо обеспечивать надежный поток данных между ERP, системами бухгалтерского учета, платежами, SMS, электронной почтой и сторонними платформами. Однако в распределенных архитектурах между локальными операциями с базой данных и этапом отправки события в систему очереди сообщений могут возникать кратковременные сбои.

Хотя транзакция базы данных успешно завершается, отправка в очередь сообщений, инициируемая сразу после нее, может завершиться сбоем из-за сетевых ошибок или сбоев сервера. Это приводит к проблемам целостности данных и несогласованности между сервисами. Традиционные подходы в подобных сценариях оказываются неэффективными.

Принцип работы паттерна Transactional Outbox

Для устранения этих несоответствий используется паттерн Transactional Outbox. Основной принцип этого архитектурного шаблона заключается не в прямом подключении к целевой системе [очереди сообщений], куда будет отправлено сообщение, а в его предварительном сохранении в соответствующую таблицу базы данных (таблицу outbox) в рамках той же транзакции.

  • Бизнес-логика выполняется и таблица основных данных обновляется.
  • В границах той же транзакции данные события, которые должны быть созданы, записываются в таблицу outbox.
  • Транзакция базы данных успешно подтверждается (commit).

Благодаря этому методу основные данные и данные событий, подлежащие передаче во внешний мир, атомарно сохраняются в реляционных базах данных, таких как [postgresql]. Если база данных была обновлена, запись outbox гарантированно существует.

Пошаговая реализация с использованием PostgreSQL и очереди сообщений

Этот процесс можно надежно настроить с использованием инфраструктуры базы данных [postgresql]. Таблица Outbox должна содержать столбцы для хранения идентификатора, типа, полезной нагрузки (payload) и статуса обработки подлежащих обработке событий.

  1. Дизайн таблицы: В таблице outbox определяются поля id, aggregate_type, payload, created_at и processed.
  2. Транзакционная запись: При выполнении операции запись в таблицу основной базы данных и запись в таблицу outbox выполняются в рамках одной транзакции.
  3. Фоновый читатель (Message Relay): Отдельный фоновый процесс (с использованием методов опроса или анализа журнала транзакций — transaction log mining) периодически сканирует необработанные записи в таблице outbox.
  4. Передача в очередь: Прочитанные данные передаются во внешнюю систему [очереди сообщений], и после подтверждения передачи статус соответствующей записи в таблице outbox обновляется.

Заключение

Создание надежных интеграций в распределенных системах требует устойчивости к возможным сценариям сбоев. Паттерн Outbox позволяет предотвратить потерю данных в процессах [системной интеграции], обеспечивая синхронную и стабильную работу систем.

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

Давайте рассмотрим процесс оформления заказа клиентом на нашей платформе электронной коммерции. При сохранении заказа в базе данных также необходимо принять платеж через внешние сервисы и уменьшить запасы. Бизнес-логика обновляет таблицу заказов и одновременно записывает событие создания заказа в таблицу outbox в рамках тех же границ транзакции. Предположим, что сетевой сбой произошел сразу после фиксации (commit) базы данных. При традиционных подходах уведомление не могло дойти до очереди сообщений, и запасы могли остаться необновленными. Однако благодаря паттерну Outbox компонент чтения сообщений в фоновом режиме перехватывает необработанную запись в таблице outbox при возобновлении работы системы. Он безопасно передает запись во внешнюю очередь сообщений и обновляет ее после успешной отправки.

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

Пошаговая реализация и контрольный список

При внедрении паттерна Outbox в продакшн-среду для обеспечения надежной сквозной работы системы необходимо выполнить следующие технические шаги:

  • Проверка атомарности операций: Убедитесь, что операции с основной базой данных и записи в outbox выполняются строго в одном блоке транзакции базы данных.
  • Индексация таблиц: Оптимизируйте производительность фонового чтения, добавив подходящие индексы в поля статуса обработки и времени создания в таблице outbox.
  • Поддержка идеопотентности: Спроектируйте идеопотентную архитектуру, предполагая, что сервисы, потребляющие очередь сообщений, могут получать одно и то же событие несколько раз.
  • Управление ошибками и повторные попытки: Настройте механизмы экспоненциальной задержки (exponential backoff), чтобы система не блокировалась при сбое передачи в очередь.
  • Очередь недоставленных сообщений: Определите механизм очереди DLQ (Dead Letter Queue) для ошибочных записей, превысивших определенное количество попыток и не подлежащих обработке.
  • Прослеживаемость: Интегрируйте инфраструктуру ведения журналов и сбора метрик для отслеживания жизненного цикла записей outbox.

Ресурсы