Два устройства изменили одну запись. Что синхронизировать?

В общем списке покупок — одна упаковка молока. На телефоне количество меняют на две, пока связи нет. На планшете тот же список открыт дома: там ставят три, и сервер принимает правку. Позже телефон подключается и отправляет свою двойку.

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

Частота обновления здесь не главное. Оба устройства могут столкнуться и без потери сети. Вопрос в другом: **на основании какой версии принято решение и кто вправе разрешить расхождение?**

Пусть оба устройства получили редакцию записи `17`. Телефон локально сохраняет «установить 2 на основе редакции 17» и может сразу показать двойку. Но показывать её как уже принятую всем списком нельзя: сервер пока подтвердил только прежнее значение. Планшет первым отправляет тройку, сервер принимает её и выдаёт редакцию `18`. Приходит телефон со своей правкой, всё ещё основанной на `17`.

Если терять изменения молча нельзя, сервер не заменяет тройку двойкой. Он проверяет редакцию при записи нового значения и сообщает о конфликте. Отдельное чтение перед обновлением не годится: между действиями успеет прийти ещё одна правка. В PostgreSQL условие `WHERE revision = :base_revision` можно включить в сам `UPDATE`. Это иллюстрация контракта, не готовая реализация: нулевое обновление ещё надо отличить от удаления записи или отсутствия доступа.

Конфликт не делает планшет «правым». Сервер установил только то, что телефон не видел уже принятой тройки. Поэтому на экране нужны оба значения: «На этом устройстве — 2. В общем списке — 3». Если человек всё-таки выбирает двойку, это новая правка на основе редакции `18`, а не бесконечный повтор старой. Сервер проверит её снова: пока шёл выбор, запись могла измениться ещё раз.

Для Flutter-клиента отсюда следует вполне практическое требование. Подтверждённое сервером значение и неразрешённую локальную правку нельзя хранить как одно число. Если при конфликте записать тройку поверх единственной локальной строки, предлагать выбор уже поздно: двойка потеряна. А если обещана работа без сети, локальная правка должна пережить и закрытие приложения.

Не каждому списку нужен экран конфликта. Разные поля иногда можно объединить автоматически по правилам продукта. Можно сознательно выбрать и **last-write-wins**, если потеря прежнего значения допустима. Но для двух и трёх упаковок нет универсальной арифметики: сложение даст пять, хотя никто не просил пять. Политика разрешения здесь важнее частоты фоновых запросов.

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

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

Полный разбор с серверным условием и границами вариантов — в ArkTelos Lab. Об экосистеме — ArkTelos. Каналы лаборатории: RU | EN.

Два устройства изменили одну запись. Что синхронизировать? | Сетка — социальная сеть от hh.ru