Архитектура уровня данных, использующая как localStorage, так и удаленный сервер REST.

У кого-нибудь есть идеи или ссылки на то, как реализовать уровень сохраняемости данных, который использует как локальное хранилище, так и удаленное хранилище REST:

Данные определенного клиента хранятся в localStorage (с помощью адаптера ember-data indexedDB). Локально сохраненные данные синхронизируются с удаленным сервером (используя RESTadapter ember-data).

Сервер собирает все данные от клиентов. Используя нотацию математических наборов:

Server = Client1 ∪ Client2 ∪ ... ∪ ClientN 

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

ClientX ∩ ClientY ≠ 0,  ∀ X,Y ∈ [1,N]

Вот несколько сценариев:

  • Клиент создает запись. Идентификатор записи не может быть установлен на клиенте, так как он может конфликтовать с записью, хранящейся на сервере. Поэтому вновь созданную запись необходимо зафиксировать на сервере -> получить идентификатор -> создать запись в localStorage.

  • Запись обновляется на сервере, и как следствие данные в localStorage и на сервере перестают синхронизироваться. Только сервер знает об этом, поэтому архитектура должна реализовать push-архитектуру (?)

Будете ли вы использовать 2 хранилища (одно для localStorage, одно для REST) ​​и синхронизировать между ними или использовать гибридный адаптер indexedDB/REST и писать код синхронизации внутри адаптера?

Видите ли вы какой-либо способ избежать реализации push (веб-сокеты,...)?


person Panagiotis Panagi    schedule 27.11.2012    source источник
comment
Эта статья дает некоторое представление: engineering.linkedin. ком/мобильный/   -  person Panagiotis Panagi    schedule 28.11.2012


Ответы (1)


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

Во-первых, есть ряд трудностей с принятым вами подходом:

  1. Клиенты всегда должны быть подключены к сети для создания данных и получения ключей от сервера.
  2. Если вы создаете разные хранилища (localstorage и REST), весь код приложения, требующий данных, должен просматриваться в обоих хранилищах. Это значительно увеличивает сложность каждой части приложения.
  3. После создания строки, если вы хотите создать дочерние строки, вы должны дождаться, пока сервер вернет первичный ключ, прежде чем вы сможете ссылаться на него как на внешний ключ в дочерней строке. Для любых умеренно сложных структур данных это становится тяжелым бременем.
  4. Когда сервер выходит из строя, все клиенты не могут создавать данные.

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

ПЕРВОЕ: используйте UUID для первичных ключей.

Большинство клиентских систем управления данными должны обеспечивать способ создания универсальных уникальных идентификаторов. SequelSphere делает это просто с помощью функции SQL: UUID(). Наличие UUID в качестве первичного ключа для каждой строки позволяет генерировать первичные ключи на любом клиенте в любое время без необходимости связываться с сервером и при этом гарантировать уникальность идентификаторов. Следовательно, это также позволяет приложению работать в автономном режиме, не требуя подключения к серверу во время выполнения. Это также предотвращает отказ сервера от отключения всех клиентов.

ВТОРОЕ: Используйте один набор таблиц, отражающих сервер.

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

ТРЕТИЙ. Для нисходящей синхронизации небольших наборов данных предпочтительнее полностью обновлять клиентские данные с сервера.

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

ЧЕТВЕРТОЕ. Для нисходящей синхронизации больших наборов данных выполняйте «транзакционные» обновления

Здесь мой подход становится немного сложнее. Если наборы данных слишком велики и требуют синхронизации только измененных строк, вы должны найти способ их синхронизации в соответствии с «транзакциями». То есть: вставки/обновления/удаления в том порядке, в котором они выполнялись на сервере, чтобы предоставить простой сценарий для выполнения того же на клиенте.

Желательно иметь на сервере таблицу, записывающую транзакции, которые необходимо синхронизировать с устройством. Если это невозможно, то заказ часто может быть записан на сервере с использованием временных меток в строках и с запросом клиента обо всех изменениях с определенной временной метки. Большой минус: вам нужно будет отслеживать удаленные строки либо путем «логического» удаления, либо путем отслеживания их в их собственной таблице. Тем не менее, изоляция сложности на сервере предпочтительнее, чем распространение ее на всех клиентов.

ПЯТОЕ: для восходящей синхронизации используйте «транзакционные» обновления

Именно здесь SequelSphereDB действительно эффективен: он будет отслеживать для вас все вставки, обновления и удаления, выполненные в таблицах, а затем предоставлять их вернуться к вам во время синхронизации. Он делает это даже при перезапуске браузера, поскольку сохраняет информацию в localstorage/indexeddb. Он даже обрабатывает коммиты и откаты соответствующим образом. Клиентское приложение может взаимодействовать с данными, как обычно, не задумываясь о записи изменений, а затем использовать «Change Trackers» SequelSphereDB для воспроизведения изменений во время синхронизации.

Если вы не используете SequelSphere (а должны), то держите на клиенте отдельную таблицу для записи всех вставок, обновлений и удалений, которые выполняет клиент. Всякий раз, когда клиентское приложение вставляет/обновляет/удаляет строки, создайте их копию в таблице «транзакций». Во время восходящей синхронизации отправьте те. На сервере просто выполните те же действия в том же порядке, чтобы реплицировать данные, которые были на клиенте.

ТАКЖЕ ВАЖНО: всегда выполняйте восходящую синхронизацию перед полным обновлением клиентских таблиц с сервера. :)

Заключение

Я предлагаю стремиться к простоте, а не сложности в максимально возможном количестве мест. Использование UUID для первичных ключей чрезвычайно полезно для этого. Также очень полезно использовать своего рода «трекеры изменений». Использование такого инструмента, как SequelSphereDB, для отслеживания изменений наиболее полезно, но не обязательно для этого подхода.

ПОЛНОЕ РАСКРЫТИЕ: я тесно связан с компанией SequelSphere, но этот продукт действительно не нужен для реализации описанного выше подхода.

person John Fowler    schedule 29.11.2012
comment
Спасибо, Джон, отличные идеи. Я начал реализовывать прототип и пришел (более или менее) к тем же выводам, что и вы. Но вы их лучше обосновали :) Однако я не уверен, что нужно фиксировать каждое изменение записи на клиенте. Разве записи не изменяются без памяти? При этом вам нужно знать только последнее состояние записи. Что я сделал, так это создал таблицу unsynced на клиенте и сохранил все записи, которые были изменены и еще не зафиксированы на сервере. Как только запись фиксируется и синхронизируется с сервером, она удаляется из unsynced. Есть ли в этом недостатки? - person Panagiotis Panagi; 30.11.2012
comment
Действительно, создание идентификаторов на клиенте — это путь. Одним из недостатков является то, что Javascript не может генерировать действительно глобально уникальные идентификаторы, как указано здесь stackoverflow.com/a/105074/359104. Но, конечно, это можно превзойти, если сервер проверяет идентификаторы и немедленно возвращает любые измененные идентификаторы. - person Panagiotis Panagi; 30.11.2012
comment
Что касается изменений без памяти: все зависит от данных. Иногда для создания дочерних строк требуется, чтобы родительские строки имели определенные значения. Или, другими словами, самый простой способ поддерживать ссылочную целостность (RI) в сложном наборе объектов — это просто воспроизвести вставки, обновления и удаления, которые произошли на клиенте. Часто для этого требуется очень мало дополнительных данных и работы, а преимущество заключается в простоте. Определить измененные строки гораздо сложнее, чем записывать изменения по мере их возникновения. Ваш подход к столу или переменам звучит хорошо. - person John Fowler; 30.11.2012
comment
Что касается GUID/UUID: при использовании алгоритмов, которые случайным образом генерируют GUID, вероятность их дублирования на клиентах является астрономической. Тем не менее, подход, который я использовал в другом проекте, заключался в том, чтобы сервер предварительно генерировал 1000 идентификаторов для каждого клиента и отправлял их клиенту для использования в любое время. Это устранило необходимость проверки на сервере, а также гарантировало уникальность. Это произошло за счет создания множества неиспользуемых идентификаторов. Но пока идентификаторы бессмысленны, это не должно быть проблемой. - person John Fowler; 30.11.2012