Лучшая практика для синхронизации распространенных распределенных данных

У меня есть интернет-приложение, которое поддерживает автономный режим, когда пользователи могут создавать данные, которые будут синхронизироваться с сервером, когда пользователь снова будет подключен к сети. Поэтому из-за этого я использую UUID для идентификации в моей базе данных, поэтому отключенные клиенты могут создавать новые объекты, не опасаясь использовать идентификатор, используемый другим клиентом, и т.д. Однако, хотя это отлично работает для объектов, принадлежащих этому пользователю, являются объектами, которые совместно используются несколькими пользователями. Например, теги, используемые пользователем, могут быть глобальными, и нет возможности, чтобы удаленная база данных могла хранить все возможные теги во вселенной.

Если автономный пользователь создает объект и добавляет к нему некоторые теги. Скажем, эти теги не существуют в локальной базе данных пользователя, поэтому программное обеспечение создает для них UUID. Теперь, когда эти теги синхронизированы, для разрешения любого совпадения потребуется процесс разрешения. Некоторые способы сопоставления любых существующих тегов в удаленной базе данных с локальными версиями.

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

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

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

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

Итак, я ищу любую лучшую практику, существующие алгоритмы для обработки этого типа системы, руководство по выбору и т.д.

Ответы

Ответ 1

В зависимости от того, какую семантику приложения вы хотите предложить пользователям, вы можете выбрать различные решения. Например, если вы на самом деле говорите об объектах тегирования, созданных автономным пользователем с ключевым словом, и хотите поделиться тегами между несколькими объектами, созданными разными пользователями, то использование текста для тега прекрасное, как вы предложили. Когда все изменения будут объединены, теги с одним и тем же "текстом", например, скажут "ЭТО УДИВИТЕЛЬНО", будут разделены.

Существуют и другие способы обработки отключенных обновлений для общих объектов. SVN, CVS и другая система управления версиями пытаются разрешить конфликты автоматически, а когда не могут, просто скажут пользователю, что есть конфликт. Вы можете сделать то же самое, просто сообщите пользователю, что были параллельные обновления, и пользователи должны обрабатывать разрешение.

Кроме того, вы также можете регистрировать обновления как единицы изменения и пытаться составлять изменения вместе. Например, если ваш общий объект является холстом, а семантика вашего приложения позволяет общий рисунок на одном холсте, то отключенное обновление, которое рисует линию от точки A до точки B, а другое отключенное обновление рисует линию от точки C до точки D, могут быть составлены. В этом случае, если вы сохраните эти два обновления как две операции, вы можете заказать два обновления и повторное соединение, каждый пользователь загружает все свои отключенные операции и применяет отсутствующие операции от других пользователей. Вероятно, вам нужно какое-то правило упорядочения, возможно, на основе номера версии.

Другая альтернатива: если обновления для общих объектов не могут быть автоматически согласованы, а семантика вашего приложения не поддерживает уведомление пользователя и просит пользователя разрешить конфликты из-за отключенных обновлений, вы также можете использовать дерево версий для его обработки. Каждое обновление для общего объекта создает новую версию с прошлой версией в качестве родителя. Когда отключены обновления общего объекта от двух разных пользователей, две отдельные дочерние версии/листовые узлы являются результатом одной и той же родительской версии. Если внутреннее представление состояния вашего приложения является этим деревом версий, то ваше внутреннее состояние приложения остается неизменным, несмотря на отключенные обновления, и вы можете обработать две ветки дерева версий каким-либо другим способом (например, дать пользователю возможность узнать о ветких и создать для них инструменты для объединения ветвей, как в системах управления версиями).

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

Ответ 2

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

Ответ 3

Ваша проблема очень похожа на системы управления версиями, такие как SVN. Вы можете взять пример из них.

Каждый пользователь будет иметь набор личных объектов и любые общие объекты, которые им нужны. Локально они будут работать так, как будто они владеют всеми объектами.

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

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

Как только клиент имеет обновленную и объединенную версию данных, вы можете сделать загрузку.

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