Implementing conflict resolution for data synchronization

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Implementing conflict resolution for data synchronization
Complex
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementing Conflict Resolution During Data Synchronization

Conflict arises when the same record is edited in two places before sync. User edited note on phone offline — and simultaneously on tablet. Both changes locally correct but contradict each other. Must decide: which wins, or how to merge them. No universal answer — strategy depends on data type and business logic.

Vector Clocks and Timestamp

Simplest strategy — Last Write Wins (LWW): newer timestamp wins. Obvious downside — on client clock skew, wrong version wins. Client clocks unreliable: user can change device time.

Reliable variant — server time. Client doesn't trust its clock; server assigns timestamp on write. Then LWW works correctly.

More advanced — Vector Clocks. Each client has ID, each change tracked by version vector:

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)
}

If clockA.happensBefore(clockB), version B is later, use it. If isConcurrentWith, conflict, need manual or automatic resolution.

CRDT for Automatic Merging

CRDT (Conflict-Free Replicated Data Types) — data structures safely merged without conflicts mathematically. Several types:

  • G-Counter — increment only. Each device stores its counter, total is sum. Applies to view counts, likes.
  • LWW-Register — Last Write Wins register via timestamp. Primitive but works for atomic values.
  • OR-Set — set of elements where add and remove don't conflict.
// 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)
        })
}

For production CRDT use in mobile apps, ready libraries exist: Automerge (Rust-core, Swift and Kotlin ports) and Yjs (JavaScript, works via React Native).

Three-Way Merge

Best approach for text content — like Git. Need common base (version before divergence), changes from client A and client B.

data class DocumentVersion(
    val id: String,
    val baseVersion: Long,   // version changes calculated from
    val content: String,
    val patches: List<Patch> // list of changes from 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
            )
        }
    }
}

On auto merge, apply both edits. On overlap, ask user to choose or edit manually.

Server-Side Conflict Logic

Client on sync sends:

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

Server checks current version. If current = baseVersion, clean merge, no conflict, apply changes. If current > baseVersion, someone changed after our base. Server returns:

{
  "status": "conflict",
  "serverVersion": 10,
  "serverContent": "...",
  "baseContent": "...",
  "clientVersion": 9
}

Client gets conflict and runs 3-way merge locally.

Conflict Resolution Strategies by Data Type

Data Type Recommended Strategy
Notes, documents 3-way merge, manual resolution on overlap
User settings LWW with server time
Counters (likes, views) G-Counter CRDT
Shopping cart OR-Set CRDT (union both versions)
Order status Server wins — server is authoritative
Map position LWW

Strategy choice is product, not technical: is losing user data worse than duplicates?

Storing Version History

For correct conflict resolution you need history. Minimum: store baseVersion and deltas from it. On deep merge, full history or snapshots.

@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 from prev version
    val authorClientId: String,
    val createdAt: Long
)

History grows. Need compression strategy: save snapshot every N versions, delete intermediate after N days.

Conflict resolution implementation with 3-way merge, CRDT or LWW depending on data types: 3–6 weeks. Server part separate estimate. Cost calculated individually.