LINUX.ORG.RU

Синхронизация истории в мессенджерах

 , ,


0

1

Допустим, мы пилим некий аналог скайпа, умеющий в синхронизацию истории переписки без центрального сервера.

Как вы бы реализовали сабж?

Я придумал такой вариант - каждая сторона считает хэш от всей локально сохранённой истории переписки и потом они обмениваются этими хэшами. Если они не совпадают, считаются хэши от половинок истории, снова сравниваются, и так далее, пока не дойдёт до отдельных сообщений, после чего сообщения, отсутствующие у другой стороны, отсылаются пачкой.

Есть идеи получше?

★★★★★

считаются хэши от половинок истории

Теперь подумай, что будет, если у одногй стороны 10000 сообщений, а у другой 10002

disarmer ★★★
()

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

mashina ★★★★★
()
Ответ на: комментарий от Harald

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

mashina ★★★★★
()

Зачем?

anonymous
()

Может просто помнить время последней синхронизации с конкретным клиентом и обмениваться всеми сообщениями позднее этого времени?

torvn77 ★★★★★
()

Сначала подумал - для Tox, что ли? Потом увидел имя автора и понял, что для мессенжера авторского разлива :)

man three way merge. Так работают синхронизаторы почтовых архивов (offlineimap, mbsync), так работает syncthing на директориях, так работает и слияние кода в VCS.

Andrey_Utkin ★★
()
Ответ на: комментарий от Andrey_Utkin

А, чёрт, нет общего предка, three way merge не подходит.

Ну, можно сделать аналог прогона rsync без флага --delete в обоих направлениях. Везде добавить всё отсутствующее. Вроде просто реализуемо.

Andrey_Utkin ★★
()
Ответ на: комментарий от Andrey_Utkin

не пали контору :)

по ходу да, придётся в алгоритм rsync-а вникать

Harald ★★★★★
() автор топика

Для каждой записи хранишь метаданные в виде (реплика_ид, запись_ид). А так же максимальное значение запись_ид для всех известных реплик.

Реплика_ид задаёт уникальный идентификатор конкретного устройства.

Запись_ид - локальный счетчик который инкрементится когда приходит новое сообщение в текущую реплику.

Когда нужно провести синхронизацию для каждой реплики выбираешь все записи где запись_ид > максимальной известной записи для выбранной реплики.

anonymous
()
Ответ на: комментарий от anonymous

А так же максимальное значение запись_ид для всех известных реплик.

а вот про это поподробнее, максимальное значение тоже как-то синхронизировать надо

при этом каждая сторона ведёт свою историю локально, запись_ид с нуля на каждом инстансе начинается. Или нет?

Harald ★★★★★
() автор топика
Ответ на: комментарий от Harald

Запись_ид в каждой реплике свой, начинается например с 1 для удобства. В процессе синхронизации реплика может получить записи с других реплик. Но у них будет другое значение реплика_ид, таким образом пара (реплика_ид, запись_ид) задаёт глобальный уникальный идентификатор записи.

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

anonymous
()
Ответ на: комментарий от anonymous

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

Harald ★★★★★
() автор топика
Ответ на: комментарий от Harald

Ты ничего не понял из моего объяснения :-)

Передаётся только то чего не хватает в текущей реплике. При передаче записей между репликами их глобальный идентификатор (реплика_ид, запись_ид) не меняется, он назначается один раз когда сообщение получает одно из устройств.

Вот тебе псевдокод для синхронизации в одну сторону. В обратную аналогично.

var thisReplica = ..  // реплика, куда льем записи
var otherReplica = .. // реплика, откуда льем записи

// Перебираем последние известные сообщения 
for (var (replicaId, recordId) in otherRelica.getKnownReplicas())
{
    // получаем последнее известное сообщение
    var thisRecordId = thisReplica.getLastKnownId(replicaId);

    // проверяем что есть неизвестные сообщения
    if (thisRecordId < recordId)
    {
        // получаем все сообщения с recordId > thisRecordId
        var records = otherReplica.getRecordsSince(replicaId, thisRecordId);
        
        // сохраняем новые сообщения
        thisReplica.addRecords(records);

        // обновляем информацию о последнем известном сообщении
        thisReplica.setLastKnownId(replicaId, recordId);
    }
}
anonymous
()

А хранить данные во временной таблице вида

  • ID чата
  • ID сообщения
  • ID клиента (кому должно упасть)
  • Status

Не вариант?

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

expelled ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.