2013-08-29 2 views
3

Давайте рассмотрим пример js «app», который в основном делает CRUD, поэтому он создает, обновляет и удаляет (на самом деле) некоторые «записи».Разрешение конфликтов на основе временной метки без надежной синхронизации времени

В самом базовом случае не нужно разрешать конфликты в таком приложении, так как свойства ACID СУБД используются для выравнивания параллельных обновлений (я просматриваю более тонны деталей здесь, я знаю). Когда нет возможности эмулировать серийное выполнение обновлений, можно использовать временные метки, чтобы определить, какое обновление «выигрывает». Даже тогда клиенту не нужно беспокоиться о временных меток, потому что они могут быть созданы во время запроса на сервере.

Но что, если мы сделаем это еще на один шаг и разрешим обновлениям очереди на клиенте на некоторое неопределенное количество времени (скажем, чтобы приложение работало, когда нет сетевого подключения), а затем выталкивается на сервер ? Тогда временная метка не может быть сгенерирована на сервере, поскольку время, когда обновление было перенесено на сервер, и фактическое время выполнения обновления может сильно различаться.

В идеальном мире, где синхронизируются все часы, это не проблема - просто создайте метку времени на клиенте в момент выполнения обновления. Но на самом деле время часто сходит с «серверного» времени (которое считается идеальным, в конце концов, его конфигурация сервера, что может с ним поделать?), Или просто неверно по часам (возможно когда вы не устанавливаете часовой пояс, а вместо этого обновляете время/дату системы, чтобы они совпадали). Что можно было бы сделать, чтобы объяснить реальность в таком случае?

Возможно, есть какой-то другой способ разрешения конфликтов, который может быть использован в таком случае?

+1

Даже в идеальном мире всех синхронизированных часов разрешение конфликтов при очередном обновлении является нерешенной проблемой. Пользователи получат неожиданные и необъяснимые результаты. Единственный способ справиться с этой проблемой - оставить обновление в «ожидающем» состоянии и сообщить об этом пользователю. –

+0

Примером должно быть «разрешить приложению работать, когда нет активности сети (бездействия)». – user568109

+0

@ user568109: Это пример _one_. Проблема немного более общая, чем – shylent

ответ

4

Ваш вопрос имеет два аспекта:

  1. Синхронизация/сериализации на сервере, используя временные метки с помощью ACID свойств базы данных.
  2. Очереди с клиентом (задержки, о которых сервер не знает).

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

Сфера действия ACID ограничена здесь, потому что, если клиентские обновления не являются в реальном времени, он не может сериализоваться в зависимости от созданной даты запроса или запроса прибытия. Он создает сценарий, когда запрос R2, созданный позже запроса R1, прибывает до R1.

Время - относительная концепция, используя локальное время для клиента или для сервера, вызывает drift для другого. Также он не масштабируется (неэффективен, если у вас несколько узлов-узлов - распределены). И он вводит одну точку отказа.

Для решения этой проблемы были разработаны vector-clocks. Это логические часы, которые увеличивают количество часов, когда событие происходит на машине атомарно. Базовые базы данных (в основном доступны, Мягкое состояние, Конечная согласованность) используют его.

В результате 1 и 2 никогда не будут работать. Вы никогда не должны ставить в очередь запросы, которые используют временную метку для разрешения конфликтов.

+0

Большое спасибо за пищу для размышлений. – shylent

1

Nice вызов. В то время как я оценил идею user568109, так я справился с аналогичной ситуацией в приложении CQRS/DDD.

В то время как в приложении DDD У меня было много разных команд и запросов, в приложении CRUD, для каждого типа «запись» мы имеем CREATE, UPDATE и DELETE команд и запросов считывают.

В моей системе на клиенте я отслеживал предыдущую синхронизацию в кортеже, содержащую: время UTC на сервере, время на клиенте (позволяет позвонить по этому адресу LastSync).

READ
Читать запрос не на Syncronization оформить участие. Тем не менее, в какой-то ситуации вам может потребоваться отправить на сервер команду LogRead, чтобы отслеживать информацию, которая использовалась для принятия решений. Такие команды содержали тип сущности, идентификатор объекта и LastSync.ServerTime).

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

UPDATE
команды Update немного сложнее, так как вы, вероятно, следует обращаться с ними по-разному на разных типов записей. Чтобы это было просто, вы должны иметь возможность навязывать пользователям, что последнее обновление всегда выигрывает, и разрабатывать команду для переноса только свойств, которые необходимо обновить (как и инструкция SQL UPDATE). В противном случае вам придется обрабатывать автоматическое/ручное слияние (но поверьте мне, это ночная жизнь: большинство пользователей этого никогда не поймут!) Сначала мой клиент потребовал эту функцию для большинства объектов, но через некоторое время они согласились с тем, что последний чтобы выиграть, чтобы избежать такой сложности. Кроме того, в случае обновления на удаленном объекте вы должны уведомить об этом ситуацию и, в соответствии с типом обновленной сущности, применить обновление или нет.

УДАЛИТЬ
команды Detele должны быть простыми, если вы не уведомлять пользователя о том, что обновление произошло, что могло бы привести его сохранить запись, а не удалять его.

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

ПРОЦЕСС SYNC
синхронизации сеанс должен начать отправку на сервер сообщение с

  • Текущее время на клиенте
  • LastSync

Таким образом, сервер может рассчитать смещение между своим временем и клией nt и применять такое смещение для каждой команды, которую он получает. Кроме того, он может проверить, изменилось ли смещение после LastSync и выберите стратегию для обработки этого изменения. Обратите внимание, что таким образом сервер не будет знать , когда часы клиента были скорректированы.

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

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

+0

Хотя ваш ответ содержит ценную информацию, я уже немного разбирался в этом вопросе (в основном это разрешение на основе временной метки с завихрением), в то время как ответ user568109 научил меня чему-то совершенно новому для меня (концепция логического времени). Большое спасибо за усилия, хотя, грустно, что я не могу вознаградить вас обоих. Сожалею! – shylent

+0

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

Смежные вопросы