Синхронизация данных между несколькими иногда подключенными клиентами с помощью EventSourcing (NodeJS, MongoDB, JSON)

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

Я знаю, что это не технический вопрос, больше концептуальная один.

Я бы просто отправил все события на сервер, но клиенты предназначены для использования в автономном режиме время от времени.

Это основной концепция:Visual Concept

на сервер хранит все события, о которых должен знать каждый клиент, он не воспроизводит эти события для обслуживания данных, потому что основная цель-синхронизировать события между клиентами, позволяя им воспроизводить все события локально.

на клиенты имейте свой один магазин JSON, также держа все события и восстановление всех различных коллекций со хранить/синхронизировать события.

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

Итак, основной проблемой для меня является определение отличия между клиентом и сервером, чтобы избежать отправки всех событий на сервере. Я также возникли проблемы с процессом синхронизации: изменения во-первых, сначала потяните изменения?

то, что я в настоящее время построил,-это реализация MongoDB по умолчанию на стороне сервера, которая изолирует все документы определенной группы пользователей во всех моих запросах (в настоящее время только обработка аутентификации и работа базы данных на стороне сервера). На клиенте я построил оболочку вокруг хранилища NeDB, что позволяет мне перехватывать все операции запроса для создания и управления событиями для каждого запроса, сохраняя при этом поведение запроса по умолчанию. Я также компенсировал различные системы идентификаторов neDB и MongoDB путем реализации пользовательских идентификаторов, которые генерируются клиентами и являются частью данных документа, так что воссоздание базы данных не испортит идентификаторы (при синхронизации эти идентификаторы должны быть согласованы между всеми клиентами).

формат события будет выглядеть примерно так:

{
   type: 'create/update/remove',
   collection: 'CollectionIdentifier',
   target: ?ID, //The global custom ID of the document updated
   data: {}, //The inserted/updated data
   timestamp: '',
   creator: //Some way to identify the author of the change
}

чтобы сохранить некоторую память на клиентах, я создам моментальные снимки при определенном количестве событий, так что полное воспроизведение всех событий будет больше эффективный.

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

но у меня проблемы с:

  • процесс определения что события для отправки от клиента при синхронизации (избегайте отправки повторяющихся событий или даже всех событий)
  • определения что события для отправки обратно клиенту (избегайте отправки повторяющихся событий или даже всех событий)
  • правильный порядок синхронизации событий (Push / Pull изменения)

еще один вопрос, который я хотел бы задать, является ли хранение обновлений непосредственно в документах в стиле ревизии более эффективным?

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

спасибо заранее!

2 ответов


это очень сложная тема, но я попытаюсь ответить в какой-то форме.

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

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

ядра баз данных, такие как Cassandra или Scylla, используют 3 сообщения за раунд слияния.

демонстрация:

данные в узел A

{ id: 1, timestamp: 10, data: { foo: '84' } }
{ id: 2, timestamp: 12, data: { foo: '23' } }
{ id: 3, timestamp: 12, data: { foo: '22' } }

данные в узле B

{ id: 1, timestamp: 11, data: { foo: '50' } }
{ id: 2, timestamp: 11, data: { foo: '31' } }
{ id: 3, timestamp: 8, data: { foo: '32' } }

Шаг 1: SYN

в нем перечислены идентификаторы и последние метки времени upsert всех документов (не стесняйтесь изменять структуру этих данных пакеты, здесь я использую подробный JSON, чтобы лучше проиллюстрировать процесс)

Node A -> Node B

[ { id: 1, timestamp: 10 }, { id: 2, timestamp: 12 }, { id: 3, timestamp: 12 } ]

Шаг 2: ACK

после получения этого пакета узел B сравнивает полученные временные метки с собственными. Для каждого документа, если отметка времени старше, просто поместите ее в полезную нагрузку ACK, если она новее, поместите ее вместе с данными. И если временные метки одинаковы, ничего не делайте- очевидно.

Node B -> Node A

[ { id: 1, timestamp: 11, data: { foo: '50' } }, { id: 2, timestamp: 11 }, { id: 3, timestamp: 8 } ]

Шаг 3: ACK2

узел A обновляет документ, если данные ACK предоставлены, а затем отправляет последние данные в узел B для тех, где данные ACK не были предоставлены.

Node A -> Node B

[ { id: 2, timestamp: 12, data: { foo: '23' } }, { id: 3, timestamp: 12, data: { foo: '22' } } ]

таким образом, оба узла теперь имеют последние данные, объединенные в обоих направлениях (в случае, если клиент сделал автономную работу) - без необходимости отправлять все ваши документы.

в вашем случае, ваш источник правда-это ваш сервер, но вы можете легко реализовать одноранговую сплетню между вашими клиентами с помощью WebRTC, например.

надеюсь, это поможет в некотором роде.

Кассандра обучающее видео

Сцилла объяснение


Я думаю, что лучшее решение, чтобы избежать всех проблем с порядком событий и дублированием, - использовать метод pull. Таким образом, каждый клиент поддерживает свое последнее импортированное состояние события (например, с трекером) и запрашивает у сервера события, созданные после последнего.

интересной проблемой будет обнаружение нарушения бизнес-инвариантов. Для этого вы также можете хранить на клиенте журнал применяемых команд и в случае конфликта (события были сгенерированы другие клиенты) вы можете повторить выполнение команд из журнала команд. Это необходимо сделать, потому что некоторые команды не будут успешными после повторного выполнения; например, клиент сохраняет документ после того, как другой пользователь удалил этот документ в то же время.