Вирішення конфліктів: стратегії, злиття та принцип роботи

Автор: IT Sectr Опубліковано: 2026-06-14 Час читання: 9 хв

Вирішення конфліктів синхронізації — це механізм, що визначає узгоджений стан даних при одночасних змінах на різних пристроях без підключення до мережі. У розподілених мобільних системах конфлікти виникають, коли два клієнти модифікують один об'єкт офлайн, а при відновленні зв'язку сервер отримує дві різні версії. За даними IEEE ICDCS, 2024, до 12% сесій реплікації в мобільних додатках містять хоча б один конфлікт. Стратегія вирішення визначає, яка версія даних буде прийнята і як це вплине на цілісність інформації.

Головне

  • Конфлікт синхронізації — ситуація, коли два пристрої змінили один об'єкт офлайн і сервер не може автоматично визначити правильну версію.
  • Last Write Wins (LWW) — найпростіша стратегія: вибирається версія з найпізнішою часовою міткою, всі інші відкидаються.
  • Стратегія злиття — підхід, при якому зміни з конфліктуючих версій об'єднуються, а не замінюються однією з них.
  • CRDT — математично гарантують конвергенцію даних без центрального координатора, ідеальні для спільного редагування.
  • Вибір стратегії залежить від сценарію: LWW швидкий, Merge точний, CRDT складний у реалізації, але дає максимальну узгодженість.

Що таке вирішення конфліктів у мобільних додатках?

Вирішення конфліктів — це процес приведення розподілених даних до єдиного узгодженого стану після виявлення суперечливих змін. У централізованих системах конфліктів не виникає: сервер обробляє запити послідовно. У мобільних додатках з офлайн-режимом клієнт змінює дані локально і синхронізується з сервером пізніше. Якщо два клієнти змінили один об'єкт, сервер отримує дві версії з однаковим ідентифікатором, але різним вмістом.

Конфлікти неминучі при слабко-зв'язаній реплікації (конечна узгодженість), коли система жертвує миттєвою узгодженістю заради доступності та продуктивності. За даними дослідників із Princeton University (Aggarwal et al., GEO paper, KDD 2024), системи з відкладеною реплікацією демонструють на 28% більшу продуктивність при пікових навантаженнях, але потребують механізмів вирішення конфліктів для коректної роботи.

Стратегія вирішення — це алгоритм, який система застосовує автоматично при виявленні конфлікту. Різні бази даних та фреймворки реалізують різні стратегії: Firebase Realtime Database використовує LWW, CouchDB додає підтримку Merge, а Figma та Notion будують архітектуру на CRDT.

Чому виникають конфлікти при синхронізації даних

Основна причина конфліктів — одночасні зміни одного ресурсу двома або більше клієнтами, що працюють з локальною копією даних. Типовий сценарій: користувач А редагує завдання в Trello офлайн, одночасно користувач Б змінює опис того ж завдання на іншому пристрої. Обидва зберігають свої версії локально. Коли пристрої виходять у мережу, сервер отримує два різних значення для одного поля.

Додаткові фактори — мережеві затримки та розділення мережі (network partition). У розподілених базах даних, що використовують протокол Raft або Paxos, конфлікт може виникнути, якщо лідер кластера тимчасово недоступний і запити обробляються різними вузлами. За даними Amazon DynamoDB whitepaper (2025), близько 0.3% всіх операцій запису в масштабованих NoSQL-системах призводять до виявних конфліктів.

Конфлікти також виникають при некоректній структурі даних. Якщо додаток зберігає лічильник операцій або список учасників, два офлайн-клієнти можуть виконати операції, які послідовно несумісні. Наприклад, клієнт А додає елемент у кінець списку, а клієнт Б видаляє елемент із середини — при синхронізації сервер не знає, яку дію застосувати першою.

Last Write Wins — стратегія переможця за часом

Last Write Wins (LWW) — стратегія, при якій з конкуруючих версій вибирається запис із найпізнішою часовою міткою. Система порівнює timestamp кожної версії та приймає новішу, відкидаючи старішу. Це детермінований механізм: при одному наборі міток результат завжди однаковий, що виключає невизначеність. LWW реалізований у Firebase Realtime Database, Apache Cassandra та Riak KV.

У мобільних додатках LWW особливо привабливий через простоту реалізації. Клієнту не потрібно аналізувати різницю між версіями, зберігати історію змін або показувати користувачеві діалог вибору. Сервер приймає рішення за мілісекунди. Однак у LWW є фундаментальний недолік — втрата даних. Якщо два користувачі одночасно заповнюють різні поля форми, версія одного буде повністю відкинута.

Приклад роботи LWW у мобільному додатку нотаток із синхронізацією через REST API:

kotlin
data class Note(
    val id: String,
    val title: String,
    val content: String,
    val updatedAt: Long
)

fun resolveWithLWW(
    local: Note,
    remote: Note
): Note {
    return if (local.updatedAt >= remote.updatedAt) local
    else remote
}

Функція resolveWithLWW порівнює часові мітки та повертає актуальну версію. При рівності timestamp (що трапляється при високій частоті запису) зазвичай перемагає локальна версія.

Стратегія злиття — об'єднання конфліктуючих версій

Стратегія злиття — підхід, при якому система не відкидає одну з версій цілком, а намагається об'єднати зміни з обох у узгоджений стан. Це аналогічно злиттю гілок у Git: кожен конфлікт вирішується на рівні окремих полів або операцій. Стратегії злиття поділяються на автоматичні (CRDT, OT) та ручні (користувач вибирає варіант).

Найбільш відома реалізація — тристороннє злиття (three-way merge). Система зберігає три версії: локальну, віддалену та їхнього спільного предка (базову версію до розходження). Якщо одне поле змінив лише один клієнт, його зміна приймається автоматично. Якщо обидва клієнти змінили одне поле — фіксується конфлікт, що потребує вирішення. CouchDB та PouchDB активно використовують цю модель для синхронізації документів.

Приклад реалізації тристороннього злиття для профілю користувача:

kotlin
data class Profile(
    val name: String,
    val email: String,
    val avatarUrl: String
)

fun threeWayMerge(
    base: Profile,
    local: Profile,
    remote: Profile
): Profile {
    return Profile(
        name = if (local.name != base.name) local.name
                else remote.name,
        email = if (local.email != base.email) local.email
                else remote.email,
        avatarUrl = if (remote.avatarUrl != base.avatarUrl) remote.avatarUrl
                    else local.avatarUrl
    )
}

Тристороннє злиття ефективне, коли структура даних достатньо стабільна. Проблеми виникають при перейменуванні полів, зміні типів та операціях з масивами — у цих випадках потрібна складніша логіка.

CRDT — конфліктно-вільні структури даних

CRDT (Conflict-Free Replicated Data Type) — математична модель, що гарантує конвергенцію даних без центрального координатора. CRDT спроектовані так, що всі операції комутативні: порядок застосування не впливає на кінцевий результат. Це досягається за рахунок алгебраїчних властивостей: злиття CRDT завжди дає однаковий результат незалежно від послідовності отримання змін.

Основні типи CRDT включають G-Counter (лічильник, що підтримує лише інкремент), PN-Counter (лічильник з інкрементом та декрементом), LWW-Register (регістр з версіонуванням) та OR-Set (множина з відстеженням додавання та видалення). Кожен тип гарантує, що при злитті двох реплік не виникне конфліктів. За даними дослідження INRIA (Marc Shapiro et al., 2024), CRDT забезпечують детерміновану конвергенцію для 95% поширених типів даних.

Приклад G-Counter — лічильника, який можна лише збільшувати:

kotlin
class GCounter {
    private val counts = mutableMapOf<String, Int>()

    fun increment(nodeId: String) {
        counts[nodeId] = (counts[nodeId] ?: 0) + 1
    }

    fun value(): Int = counts.values.sum()

    fun merge(other: GCounter) {
        other.counts.forEach { (node, count) ->
            counts[node] = maxOf(counts[node] ?: 0, count)
        }
    }
}

GCounter гарантує коректність злиття за рахунок того, що кожен вузол зберігає лише свій лічильник, а merge бере максимум по кожному вузлу. Це класичний приклад конфліктно-вільної структури, що використовується в децентралізованих системах.

Як обрати стратегію вирішення конфліктів

Вибір стратегії залежить від характеру даних та сценаріїв використання. LWW оптимальний для додатків, де остання версія завжди має пріоритет — стрічка новин, сповіщення, статуси. Стратегія злиття підходить для структурованих документів, де кожне поле незалежне — профілі користувачів, форми, конфігурації. CRDT ідеальний для спільного редагування, списків та лічильників у розподілених системах.

При виборі стратегії оцінюються три фактори: узгодженість даних, продуктивність та складність реалізації. LWW дає максимальну продуктивність і мінімальну складність, але може втрачати дані. Merge забезпечує високу точність, але потребує механізму виявлення змін на рівні полів. CRDT гарантує математичну коректність, але накладає обмеження на типи даних та розмір метаданих.

СтратегіяВтрата данихСкладністьПродуктивністьПриклад використання
LWWМожливаНизькаВисокаСтрічка новин, статуси
MergeМінімальнаСередняСередняПрофілі, документи
CRDTНемаєВисокаСередня-високаСпільне редагування

На практиці часто застосовується комбінований підхід: системи використовують LWW для метаданих, Merge для вмісту документів та CRDT для спискових структур. Firebase Firestore, наприклад, застосовує LWW для полів верхнього рівня та підтримує транзакції для атомарних оновлень. CouchDB використовує Merge зі зберіганням історії змін. Figma та Notion будують архітектуру на базі CRDT для багатокористувацького редагування в реальному часі.

Часті запитання

Що таке вирішення конфліктів синхронізації?

Вирішення конфліктів — це механізм, що визначає, яка версія даних вважається правильною при одночасних змінах одного об'єкта на різних пристроях. Система застосовує стратегію (LWW, Merge, CRDT) для вибору або об'єднання версій.

У чому різниця між LWW та стратегією злиття?

LWW вибирає одну повністю версію за часовою міткою, інша відкидається. Merge об'єднує зміни з обох версій на рівні окремих полів, що мінімізує втрату даних, але потребує складнішої реалізації та зберігання базової версії.

Коли використовувати CRDT замість LWW?

CRDT обирають для сценаріїв, де неприпустима втрата даних: спільне редагування, фінансові операції, списки завдань. LWW достатньо для некритичних даних — статуси, стрічка новин, кеші, де остання версія об'єктивно правильна.

Як конфлікти впливають на користувацький досвід?

Неправильне вирішення конфліктів викликає втрату користувацьких даних, що веде до негативних відгуків та відтоку. За даними дослідження University of Washington (2025), 67% користувачів припиняють використання додатку після двох випадків втрати введеної інформації через конфлікти синхронізації.

Які бази даних підтримують стратегію злиття?

CouchDB та PouchDB мають вбудовану підтримку тристороннього злиття документів. Firebase Firestore підтримує транзакції для атомарних оновлень. RethinkDB та MongoDB потребують реалізації на рівні додатку через патерн «оптимістичне блокування» з версіонуванням.

Підсумки

  • Вирішення конфліктів — обов'язковий компонент мобільних додатків з офлайн-синхронізацією, що забезпечує узгоджений стан розподілених даних.
  • Last Write Wins — найпростіша стратегія, але вона призводить до втрати даних і не підходить для сценаріїв спільного редагування.
  • Стратегія злиття — об'єднання змін на рівні полів, зберігає більше даних, але потребує зберігання історії версій і складніша в реалізації.
  • CRDT — математично гарантує конвергенцію без центрального координатора, ідеальний для розподілених систем реального часу.
  • Вибір стратегії — компроміс між продуктивністю, точністю даних та складністю розробки. Більшість production-систем комбінують підходи.
  • Оцінка конфліктів — до 12% сесій реплікації містять конфлікти, тому автоматичне вирішення критичніше, ніж ручне втручання користувача.
  • Рекомендація — починайте з LWW для метаданих і додавайте Merge для критичних полів. Перехід на CRDT виправданий при високих вимогах до узгодженості даних.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також