Почему шины данных не всегда лучшее решение для синхронизации систем

Вы запускаете новую ERP и настраиваете репликацию данных, развернули шину. Кажется, что все просто. Но сначала нужно реализовать в системе-источнике листенеры. Потом добавить буферные таблицы Outbox и Inbox для 100% надежности. Потом получили несколько ошибок и пришлось дописать обработку конфликтов. Чтобы вовремя отлавливать конфликты, нужен мониторинг. Затем модель данных немного изменилась, нужно вносить правки. И вот уже треть от всех усилий команды уходит на поддержание репликации, а не на разработку новой ERP. Знакомо?
При миграции из одной корпоративной системы в другую часто встает задача репликации данных. Например, при поэтапной замене ERP по методологии Parallel Running во время опытной эксплуатации пользователи работают в двух системах, которые оперируют идентичным набором данных. Если модели данных сильно отличаются, а при репликации присутствует дополнительная логика, то команды чаще всего пытаются использовать шины данных (например Kafka). При таком подходе требуется самостоятельно реализовать механизмы копирования данных, обработки конфликтов, пересинхронизации, мониторинга и т. д. Все это увеличивает трудозатраты и потенциально может стать источником проблем. В этой статье разберем техническую реализацию репликации данных через шины, ее слабые места, а также альтернативные подходы.
О каких ситуациях пойдет речь
В этой статье обсудим подходы к репликации данных при миграции корпоративных систем. Например, замена масштабной ERP идет по модулям, используется методология Parallel Running, когда старая и новая системы работают одновременно. Модуль «Договорная работа» уже реализован в новой ERP и пользователи работают с договорами именно там. Другие операции пока выполняются в старой системе, для корректной работы нужна периодическая репликация договоров.
Мы рассматриваем только сложные ситуации:
- Модель данных для одной и той же сущности в разных ERP существенно отличается.
- Присутствует сложная логика репликации, например, договоры передаются только при завершении первичной работы и выставления соответствующего статуса.
Во всех остальных случаях с задачей отлично справляются платформы типа Debezium с классическим Change Data Capture через чтение журнала транзакций.
В нашем примере репликацию договоров можно разделить на этапы:
- Захват измененных данных, которые требуется передать.
- Хранение измененных данных для последующей асинхронной передачи, т. к. синхронная передача имеет ряд недостатков (связывает доступность источника с доступностью получателя/шины, блокирует источник на время отправки и т. д.).
- Преобразование данных в формат приложения-приемника.
- Передача и применение измененных данных в приложении-приемнике.
Репликация через шину — самый распространенный подход
Очень часто команды выбирают для решения подобных задач шаблонный подход — самописный механизм репликации с использованием шины данных (Kafka, Artemis).
- Для захвата измененных данных и преобразования в нужный формат создаются обработчики событий изменения сущностей. В приложениях на Java это обычно делается с использованием JPA entity listeners.
- Данные хранятся в шине. Отправка данных в шину происходит либо напрямую из листенеров, либо с использованием промежуточной таблицы (паттерн Outbox).
- Данные передаются либо в БД-получатель, либо в отдельный сервис, который записывает их в БД-получатель. При этом чтение из шины и отправка в БД-получатель могут осуществляться как синхронно, так и с использованием промежуточной таблицы Inbox по аналогии с Outbox.

С таким подходом мы неоднократно сталкивались в проектах — примерно в 40% случаях команды хотят или пытаются использовать именно его.
Листенеры и шины кажутся простым и понятным путем, особенно если они уже используются для других задач. Обычно логика выбора пути миграции такая:
- Нужно отслеживать изменение → используем листенер.
- Изменение надо куда-то отправить → на поверхности вариант с шиной.
- Шина иногда недоступна → появляются Outbox, Inbox и другие надстройки.
Так решение становится более сложным, реализация требует больше трудозатрат, появляется больше мест, где может произойти сбой.
Техническая реализация репликации через шину
Далее мы разберем в деталях техническую реализацию репликации с шиной, а также потенциальные проблемы и сложности.
1 Связанность систем
Листенеры, которые фиксируют изменения и пишут сообщения в шину данных, встраиваются в код системы-источника. Из-за этого растет связанность.
Необходимость затрагивать кодовую базу системы-источника (в нашем случае это новая ERP) увеличивает трудозатраты.
Более того, каждое изменение правил репликации требует вмешательства в код и деплоя новой версии. Каждая новая версия — это всегда трудозатраты на сборку, тестирование и деплой. В дополнение к этому, чаще всего при развертывании новой версии приходится останавливать работу приложения и, соответственно, работу пользователей.
Если в листенере реализовано преобразование данных в формат целевой БД, то у системы-источника появляется дополнительная зависимость от системы-получателя (в нашем случае, старой ERP). При изменении модели данных в системе-получателе может потребоваться выпустить новую версию системы-источника.
2 Паттерн Outbox/Inbox
Иногда шина данных может быть недоступна по разным причинам: сетевые сбои, проблемы с кластером брокера и т. д. При непосредственной синхронной записи из обработчика событий в шину данных это может привести к потере данных. Например, изменения сохранились в источнике, но при отправке возникла ошибка. Для решения этой проблемы применяется паттерн Outbox.
Найденные при помощи листенера изменения пишутся не напрямую в шину, а в буферную таблицу. Это происходит в той же транзакции, что и изменение данных.
Отдельным процессом изменения читаются из таблицы и отправляются в шину. При недоступности брокера данные остаются в Outbox-таблице и через установленное время отправляются повторно.
На стороне приемника может применяться паттерн Inbox: данные из шины не синхронно применяются на получателе, а считываются в буферную таблицу Inbox и обрабатываются отдельным процессом.
3 Обработка конфликтов репликации
Никто заранее не думает о том, что запись найденного изменения может не пройти, но это часто происходит.
Типичные причины конфликта репликации:
- Не заполнены обязательные поля.
- Срабатывают ограничения внешнего ключа (constraint violation).
Стандартная обработка такой ситуации — возврат ошибочного сообщения в очередь DLQ, где это сообщение остается непрочитанным. В итоге системы расходятся: в источнике 10 договоров, в приемнике только 9.
Для решения этой проблемы приходится писать механизмы обработки ошибок и разрешения конфликтов самостоятельно в консьюмере шины.
4 Использование шины в качестве хранилища изменений
Без применения паттерна Inbox на работу с сообщениями репликации накладываются ограничения:
- Нет возможности обогатить сообщение дополнительной информацией: текст ошибки, код и т. д.
- Отсутствует произвольный доступ к сообщениям, переотправка единичных сообщений не по порядку невозможна.
5 Мониторинг репликации
Шина данных — это транспорт, поэтому она предоставляет мониторинг соответствующего уровня. Можно увидеть размеры очередей или пропускную способность, но прикладная картина репликации отсутствует. «Из коробки» шина не показывает, сколько изменений по конкретному потоку еще не применено и почему, какие записи в конфликте и что разошлось. Поэтому такой мониторинг приходится строить самому: собирать метрики, рисовать дашборды. Это время и усилия, и результат обычно получается не самым удобным в эксплуатации.
6 Пересинхронизация
Иногда возникает необходимость провести повторную отправку изменений из источника в получатель (например, добавился новый атрибут для передачи, нужно обновить старые записи). В случае использования описанного подхода необходимо разрабатывать отдельный механизм пересинхронизации со слиянием изменений.
Таким образом, описанный подход требует самостоятельной разработки механизма репликации. Сюда входит и реализация Outbox/Inbox, и взаимодействие с шиной данных, и логика обработки конфликтов репликации, и настройка/реализация мониторинга. В результате объем трудозатрат на репликацию данных составляет около 30% от общего объема работы.
Более того, не всегда трудозатраты удается правильно оценить на старте. Это происходит потому, что объем работы увеличивается по ходу миграции, а также из-за необходимости выпускать новую версию в случае мелких правок. Естественно, все это влияет также на сроки.
Альтернативный подход — репликация с захватом данных на уровне БД
Мы часто выполняем проекты по миграции ERP-систем с репликацией данных, поэтому реализовали готовое решение, которое можно переиспользовать, чтобы не писать каждый раз все с нуля. Позже мы выпустили его в качестве тиражируемого продукта под названием xDbStream.
Это самостоятельное веб-приложение, которое покрывает задачи каждого этапа репликации данных.

- Захват изменений осуществляется с помощью поиска измененных записей по метке времени. Для этого механизм Change Data Capture (CDC) периодически опрашивает таблицы приложения-источника на предмет измененных записей и отбирает записи, измененные позже последнего сканирования. Есть возможность отбирать только записи, которые подходят под условия, например, договоры с определенным статусом.
- Захваченные измененные данные хранятся в соответствующих таблицах БД xDbStream.
- Преобразование данных осуществляется по пользовательским скриптам.
- Передача данных осуществляется с определенной периодичностью. Есть возможность как напрямую писать в БД получателя, так и отправлять данные, например, в REST-эндпойнты.
Сравнение компонентов подхода с шиной и xDbStream:
| Компонент | Шина | xDbStream |
|---|---|---|
| Захват изменений и преобразование данных | Обработчики событий изменения сущностей | Сканирование по метке времени + механизм преобразований на основе скриптов |
| Буферная зона | Таблицы Outbox/Inbox | Служебные таблицы в БД xDbStream |
| Транспорт | Шина данных (Kafka, Artemis) | Очереди и потоки (служебные таблицы) в БД xDbStream |
| Отправка изменений | Консьюмер шины, пишущий в БД получателя | Встроенный механизм периодической отправки изменений получателю (запись в БД, REST-эндпойнт и др.) |
Основные отличия от подхода с шиной
Настройка репликации в xDbStream — это описание правил получения, преобразования и отправки измененных данных. Вся служебная функциональность (механизм запуска считывания изменений, запуска преобразования и запуска отправки, хранилище найденных изменений, мониторинг и другое) уже разработана и проверена.
1 Уменьшение связанности
Главное отличие метода и технологий: интеграция живет отдельно, сервис не лезет в код приложения. Он подключается к базам данных и выполняет поиск изменений без правок в проекте, пересборки, остановки системы. Добавлять, удалять или менять потоки репликации можно в рантайме.
2 Outbox и Inbox
Аналоги паттернов Outbox и Inbox уже реализованы в xDbStream, не нужно разрабатывать их самостоятельно. Так как xDbStream считывает изменения из БД-источника и записывает их в собственное хранилище, соответствующие буферные таблицы в источнике и получателе не нужны.
3 Обработка конфликтов
Для разрешения конфликтов в xDbStream предусмотрен механизм автоматической повторной отправки сообщений получателю.
4 Обогащение сообщений с изменениями
Хранилище данных в xDbStream — это таблицы в собственной БД. Соответственно, для служебных целей и расширенного мониторинга в записи об изменениях дополнительно пишется полезная информация об ошибках репликации, статусе сообщения и др. Также есть возможность переотправки отдельных произвольных сообщений.
5 Встроенный мониторинг
xDbStream специально разрабатывался для репликации данных, поэтому имеет расширенный мониторинг состояния репликации: количество считанных, трансформируемых, отправляемых, обработанных изменений, ошибки репликации, детальная информация о каждом сообщении, информация о последнем, следующем запусках.
Более того, в состав продукта входит подсистема сверки данных, позволяющая проверять, соответствуют ли данные приложения-источника данным получателя.
6 Пересинхронизация
Механизм пересинхронизации с определенной даты/версии доступен из коробки.
Особенности xDbStream
XDbStream не в полной мере подходит для передачи изменений в реальном времени, когда все изменения должны быть доставлены в БД-получатель в пределах нескольких секунд. Пусть даже таких изменений мало и возникают они редко. Так как сканирование происходит по метке времени, то период скана напрямую задает максимальную задержку при передаче: сканируем раз в минуту, значит изменения получаем и передаем раз в минуту. Тогда как шина с листенерами может передавать изменения очень быстро, даже с Outbox и Inbox, где время скана можно делать небольшим.
Кроме того, при использовании xDbStream в таблицах БД-источника должен быть столбец с меткой времени (с временем изменения сущности), иначе сканирование не будет работать.
Другие альтернативы шинам
Проблемы шин можно обойти и другими способами. Например, существуют популярные иностранные продукты:
- Oracle GoldenGate — одно из самых известных решений с огромным количеством адаптеров под все СУБД.
- Qlik Replicate (бывший Attunity) — тоже среди наиболее популярных решений.
Однако на российском рынке в корпоративном секторе эти решения нельзя использовать из-за политик безопасности.
В России существует Датафлот Репликация, где используется CDC на основе журнала транзакций (WAL). Однако этот продукт не предназначен для сложной пользовательской логики преобразования, эти случаи мы с самого начала оставили за скобками.
Итоги и выводы
При использовании шины время тратится на разработку и поддержку самописных механизмов, а также развертывание шины данных. Написать единичный листенер на существующей инфраструктуре несложно, но все вместе занимает время. Дополнительных трудозатрат требует внесение изменений, если изменится логика репликации данных или другие вводные. Более того, из-за сильной связности неизбежны простои во время деплоя новой версии, который затронут пользователей и бизнес-процессы. Важно понимать, что репликация по методологии Parallel Running — не разовая операция, а процесс, который работает на протяжении нескольких месяцев пока идет разработка и запуск новых модулей. Все это время команде понадобится тратить усилия на поддержку механизма репликации.
xDbStream — готовое решение, которое дает выигрыш по времени и экономию усилий. В итоге затраты сокращаются с 30% от всего бюджета до 3–5%. На типовом проекте миграции корпоративной системы с бюджетом 100 млн руб. подход с шиной потребует ~30 млн на разработку и поддержку репликации, тогда как xDbStream обойдется в 3–5 млн руб. Примерно такое соотношение мы получили в проекте миграции многомодульной ERP-системы ГазпромТранс — логистического оператора нефтегазового холдинга.