Ответ 1
Я бы предложил использовать обновления на основе ключей вместо обновлений на основе контактов.
Вы не должны отправлять весь контакт на сервер, в большинстве случаев пользователь просто изменит несколько атрибутов в любом случае (такие вещи, как "фамилия", обычно не меняются очень часто). Это также уменьшает использование полосы пропускания.
Наряду с внесенными изменениями вашего автономного контакта вы отправляете
старый номер версии/последнее обновление временной метки вашего локального контакта с сервером. Теперь сервер может
определить, обновлены ли ваши локальные данные, просто просмотрев свой старый номер версии.
Если ваш старый номер версии соответствует текущему номеру версии сервера, вам не нужно обновлять какую-либо другую информацию. Если это не так, сервер должен отправить вам новый контакт (после применения вашего запрошенного обновления).
Вы также можете сохранить эти коммиты, это приведет к истории контактов
который не сохраняет весь контакт каждый раз, когда ключ был изменен, а только сами изменения.
Простая реализация в псевдокоде может выглядеть так:
for( each currentContact in offlineContacts ) do
{
if( localChanges.length > 0){ // updates to be made
commitAllChanges();
answer = getServerAnswer();
if(answer.containsContact() == true){
// server sent us a contact as answer so
// we should overwrite the contact
currentContact = answer.contact;
} else {
// the server does not want us to overwrite the contact, so we are up to date!
}
// ...
}
} // end of iterating over contacts
Серверная сторона будет выглядеть так же просто:
for (currentContactToUpdate in contactsToUpdate) do
{
sendBackContact = false; // only send back the updated contact if the client missed updates
for( each currentUpdate in incomingUpdates ) do {
oldClientVersion = currentUpdate.oldversion;
oldServerVersion = currentContact.getVersion();
if( oldClientVersion != oldServerVersion ){
sendBackContact = true;
// the client missed some updates from other devices
// because he tries to update an old version
}
currentContactToUpdate.apply(currentUpdate);
}
if(sendBackContact == true){
sendBack(currentUpdate);
}
}
Чтобы лучше понять рабочий процесс, я приведу пример:
8 AM и клиенты, и сервер обновлены, каждое устройство находится в режиме онлайн
Каждое устройство имеет запись (в данном случае строку) для контакта "Foo Bar", которая имеет идентификатор первичного ключа. Версия для каждой записи одинакова, поэтому все они обновлены.
_ Server iPhone iPad
ID 42 42 42
Ver 1 1 1
First Foo Foo Foo
Last Bar Bar Bar
Mail [email protected] [email protected] [email protected]
(извините этот ужасный формат, SO, к сожалению, не поддерживает какие-либо таблицы...)
9 утра ваш iPhone отключен. Вы заметили, что электронная почта Foo Bar изменилась на "foo @b" .
Вы изменяете контактную информацию на своем телефоне следующим образом:
UPDATE 42 FROM 1 TO 2 [email protected]
// ^ID ^old version ^new version ^changed attribute(s)
теперь контакт на вашем телефоне будет выглядеть следующим образом:
_ iPhone
ID 42
Ver 2
First Foo
Last Bar
Mail [email protected]
10 утра ваш iPad отключен. Вы заметили, что "Foo Bar" на самом деле написан как "Voo Bar" ! Вы немедленно применяете изменения на своем iPad.
UPDATE 42 FROM 1 TO 2 First=Voo
Обратите внимание, что iPad по-прежнему считает, что текущая версия контакта 42 равна 1. Ни сервер, ни iPad не заметили, как вы изменили адрес электронной почты и увеличили номер версии, поскольку в сеть не было подключено никаких устройств. Эти изменения только локально сохранены и видны на вашем iPad.
11 AM вы подключаете iPad к сети. iPad отправляет последнее обновление
к серверу.
До:
_ Server iPad
ID 42 42
Ver 1 2
First Foo Voo
Last Bar Bar
Mail [email protected] [email protected]
iPad → Сервер:
UPDATE 42 FROM 1 TO 2 First=Voo
Теперь сервер может видеть, что вы обновляете версию 1 контакта 42. Поскольку версия 1 - это текущая версия, ваш клиент обновлен (никаких изменений не было сделано в то время, когда вы были в автономном режиме).
Сервер → iPad
UPDATED 42 FROM 1 TO 2 - OK
После:
_ Server iPad
ID 42 42
Ver 2 2
First Voo Voo
Last Bar Bar
Mail [email protected] [email protected]
12 утра вы отключили iPad от сети и подключили свой iPhone.
IPhone пытается зафиксировать последние изменения.
До:
_ Server iPhone
ID 42 42
Ver 2 2
First Voo Voo
Last Bar Bar
Mail [email protected] [email protected]
iPhone → Сервер
UPDATE 42 FROM 1 TO 2 [email protected]
Сервер уведомляет, как вы пытаетесь обновить старую версию одного и того же контакта.
Он будет применять ваше обновление, поскольку он является более поздним, чем обновление iPad, но
отправит вам новые контактные данные, чтобы убедиться, что вы также получите обновленное имя.
После:
_ Server iPhone
ID 42 42
Ver 2 2
First Voo Voo
Last Bar Bar
Mail [email protected] [email protected]
Сервер → iPad
UPDATED 42 FROM 1 TO 3 - Ver=2;First=Voo;.... // send the whole contact
/* Note how the version number was changed to 3, and not to 2, as requested.
* If the new version number was (still) 2 the iPad would miss the update
*/
В следующий раз, когда ваш iPad подключится к сети и не будет изменений для фиксации, он должен просто отправить текущую версию контакта и посмотреть, все ли он в актуальном состоянии.
Теперь вы совершили две автономные изменения, не перезаписывая друг друга.
Вы можете легко расширить этот подход и, следовательно, некоторые оптимизации.
Например:
- Если клиент пытается обновить старую версию контакта, не отправляйте им весь контакт в качестве ответа. Скорее отправьте им те коммиты, которые они пропустили, и пусть они обновляют свои контакты сами. Это полезно, если вы храните много информации о своем клиенте и ожидаете, что между обновлениями будет сделано несколько изменений.
- Если клиент обновил всю информацию о контакте, мы можем предположить, что ему не нужно знать о пропущенных обновлениях, однако мы бы сообщали ему обо всем, что он пропустил (но это было бы/не должно было повлиять на него)
Надеюсь, это поможет.