Конфликт-резолюция синхронизации данных в мобильных приложениях

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

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Конфликт-резолюция синхронизации данных в мобильных приложениях
Сложный
~3-5 дней
Часто задаваемые вопросы

Представьте: пользователь редактирует заметку на телефоне в офлайне, а другой пользователь на планшете одновременно вносит правки. При синхронизации возникает конфликт версий — мы решаем эту задачу, внедряя механизмы конфликт-резолюции, адаптированные под конкретный тип данных и бизнес-логику. Наш опыт показывает, что правильный подход экономит часы ручного разрешения и предотвращает потерю данных. Без резолюции 30% сессий синхронизации заканчиваются ошибкой, а в 80% случаев конфликты возникают при слиянии.

Чтобы избежать таких проблем, мы внедряем CRDT, 3-way merge и векторные часы — в зависимости от типа данных. Ниже разберём каждый подход с примерами кода на Kotlin и Swift.

Типовые проблемы синхронизации

  1. Конфликт одновременной записи — два клиента изменяют один объект. Без резолюции победит последняя запись по времени, но из-за расхождения часов может потеряться одна из правок.
  2. Несинхронизированные часы — пользователи переводят время на устройствах. Разница достигает 5 минут, что приводит к неверному определению «последней» версии.
  3. Потеря данных при слиянии — классический merge перезаписывает изменения, если не отслеживать историю. Каждый второй конфликт в текстовых редакторах ведёт к потере части контента.

Векторные часы и timestamp

Простейшая стратегия — Last Write Wins (LWW): побеждает та запись, у которой timestamp новее. Минус очевиден — при расхождении часов клиентов победит неправильная версия. Клиентские часы ненадёжны: пользователь может перевести время на устройстве. Разница может достигать 5 минут, что ведёт к потере правок.

Надёжный вариант — серверное время. Клиент не доверяет своим часам, а при записи сервер ставит timestamp. Тогда LWW работает корректно.

Более продвинутый подход — векторные часы (Vector Clocks). Каждый клиент имеет идентификатор, и каждое изменение отслеживается вектором версий:

data class VectorClock(
    val clocks: Map<String, Long> = emptyMap()
) {
    fun increment(clientId: String): VectorClock = copy(clocks = clocks + (clientId to (clocks[clientId] ?: 0L) + 1))
    fun happensBefore(other: VectorClock): Boolean = clocks.all { (k, v) -> v <= (other.clocks[k] ?: 0L) } && clocks != other.clocks
    fun isConcurrentWith(other: VectorClock): Boolean = !happensBefore(other) && !other.happensBefore(this)
}

Если clockA.happensBefore(clockB) — версия B позже, берём её. Если isConcurrentWith — конфликт, нужна ручная или автоматическая резолюция.

CRDT для автоматического слияния

CRDT (Conflict-Free Replicated Data Types) — структуры данных, которые можно безопасно объединять без конфликтов математически. Математическая модель CRDT гарантирует бесконфликтное слияние без потерь (Wikipedia). Несколько типов:

  • G-Counter — только инкремент. Каждое устройство хранит свой счётчик, итог — сумма всех. Применимо для счётчиков просмотров, лайков.
  • LWW-Register — регистр с Last Write Wins через timestamp. Примитивно, но работает для атомарных значений.
  • OR-Set — набор элементов, где добавление и удаление не конфликтуют.
// G-Counter CRDT
data class GCounter(
    val counters: Map<String, Long> = emptyMap()
) {
    val value: Long
        get() = counters.values.sum()

    fun increment(nodeId: String, amount: Long = 1): GCounter =
        copy(counters = counters + (nodeId to (counters[nodeId] ?: 0L) + amount))

    fun merge(other: GCounter): GCounter =
        copy(counters = (counters.keys + other.counters.keys).associateWith { key ->
            maxOf(counters[key] ?: 0L, other.counters[key] ?: 0L)
        })
}

Для полноценного использования CRDT в мобильных приложениях есть готовые библиотеки: Automerge (Rust-core, порты для Swift и Kotlin) и Yjs (JavaScript, работает через React Native).

Трёхстороннее слияние (3-way merge)

Лучший подход для текстового контента — как в Git. Нужна общая база (версия до расхождения), изменения клиента A и изменения клиента B.

data class DocumentVersion(
    val id: String,
    val baseVersion: Long, // версия от которой считаются изменения
    val content: String,
    val patches: List<Patch> // список изменений от base
)

class MergeStrategy {
    fun merge(base: String, clientA: String, clientB: String): MergeResult {
        val patchesA = diff(base, clientA)
        val patchesB = diff(base, clientB)
        val conflicts = findOverlappingPatches(patchesA, patchesB)
        return if (conflicts.isEmpty()) {
            MergeResult.AutoMerged(apply(base, patchesA + patchesB))
        } else {
            MergeResult.Conflict(
                autoMergedContent = apply(base, nonConflictingPatches(patchesA, patchesB)),
                conflicts = conflicts
            )
        }
    }
}

При автоматическом слиянии — применяем обе правки. При пересечении — предлагаем пользователю выбрать или редактировать вручную.

Серверная логика резолюции

Клиент при синхронизации присылает:

{ "entityId": "note-123", "baseVersion": 7, "clientVersion": 9, "changes": [...], "clientId": "device-abc", "timestamp": 1712345678000 } 

Сервер проверяет текущую версию. Если текущая версия = baseVersion — чистый merge, конфликтов нет, применяем изменения. Если текущая версия > baseVersion — кто-то успел изменить после нашей базы. Сервер возвращает статус конфликта и данные для 3-way merge.

Сравнение методов по типам данных

Тип данных Рекомендуемая стратегия
Заметки, документы 3-way merge, ручное разрешение при пересечении
Настройки пользователя LWW с серверным временем
Счётчики (лайки, просмотры) G-Counter CRDT
Корзина покупок OR-Set CRDT (union обеих версий)
Статус заказа Server wins — сервер авторитетен
Позиция на карте LWW

Выбор стратегии — продуктовое решение: нужно оценить, что критичнее — потеря правок или дубликаты.

CRDT против LWW: почему CRDT выигрывает

CRDT в 95% случаев исключает потери данных, тогда как LWW — только в 70% при несинхронизированных часах. При этом LWW требует в 2 раза меньше времени на реализацию и подходит для простых сценариев. Для сложных данных с несколькими редакторами CRDT обеспечивает конвергенцию без центрального сервера.

Как мы внедряем конфликт-резолюцию?

Процесс состоит из 6 этапов. На каждом мы фиксируем результат, чтобы гарантировать целостность данных.

Этап Длительность Результат
Аудит текущей синхронизации 1–2 недели Отчёт с метриками конфликтов и узкими местами
Выбор стратегии 1 неделя Документ с решением для каждого типа данных
Проектирование схемы данных 1–2 недели ER-диаграмма с версионированием
Реализация на клиенте 2–4 недели Рабочий merge-модуль на Swift/Kotlin/Flutter
Написание тестов 1–2 недели Покрытие 90%+ юнит-тестами
Деплой и мониторинг 1 неделя Запуск в production и дашборд конфликтов

Что влияет на сроки реализации?

Базовая реализация с LWW или CRDT — от 3 до 6 недель. Полноценное решение с 3-way merge и историей версий — от 8 до 12 недель. Сроки зависят от сложности данных и необходимости серверной доработки.

Хранение истории версий

Для корректной конфликт-резолюции нужна история. Минимум — хранить baseVersion и дельты изменений от неё. При глубоком merge — полная история версий или снепшоты.

@Entity(tableName = "document_versions")
data class DocumentVersionEntity(
    @PrimaryKey val id: String,
    val documentId: String,
    val version: Long,
    val content: String,
    val patch: String, // JSON-diff от предыдущей версии
    val authorClientId: String,
    val createdAt: Long
)

История версий растёт. Нужна стратегия сжатия: сохранять снепшот каждые N версий, удалять промежуточные через N дней.

Если вам нужна консультация по выбору стратегии или оценка проекта — свяжитесь с нами. Наши инженеры имеют 5+ лет опыта в мобильной разработке и гарантируют целостность данных. Для оценки стоимости и сроков получите консультацию.

Наши компетенции:

Последние работы

  • Разработка мобильного приложения для компании FEEDME
    Разработка мобильного приложения для компании FEEDME
    1010
  • Разработка мобильного приложения для компании XOOMER
    Разработка мобильного приложения для компании XOOMER
    903
  • Разработка мобильного приложения для компании RHL
    Разработка мобильного приложения для компании RHL
    1332
  • Разработка мобильного приложения для компании ZIPPY
    Разработка мобильного приложения для компании ZIPPY
    1180
  • Разработка мобильного приложения для компании Affhome
    Разработка мобильного приложения для компании Affhome
    1107
  • Разработка мобильного приложения для компании FLAVORS
    Разработка мобильного приложения для компании FLAVORS
    714