Digital Public Infrastructure
Cloud Systems

Offline-First Sync: CRDTs and Conflict Resolution

Implementing eventually consistent sync with CRDTs and merge strategies for applications in low-connectivity and last-mile environments.

Akshay MulgavkarMarch 18, 202410 min read

Offline-First Sync: CRDTs and Conflict Resolution

Applications serving users in low-connectivity regions need to work offline and sync when connectivity returns. Conflict-free replicated data types (CRDTs) and merge strategies enable eventually consistent sync without central coordination.

The Offline-First Model

Principles

  • Local-first: Data lives on device
  • Sync when possible: Background replication
  • Conflict resolution: Deterministic merge
  • No central bottleneck: Peer or server sync

Use Cases

  • Field data collection
  • Last-mile service delivery
  • Underserved communities with intermittent connectivity
  • Multi-device workflows

CRDTs: Conflict-Free Replication

CRDTs guarantee convergence: all replicas reach the same state without coordination.

G-Counter (Grow-Only Counter)

class GCounter:
    def __init__(self):
        self.counts = {}  # replica_id -> count

    def increment(self, replica_id):
        self.counts[replica_id] = self.counts.get(replica_id, 0) + 1

    def merge(self, other):
        for rid, count in other.counts.items():
            self.counts[rid] = max(self.counts.get(rid, 0), count)

    def value(self):
        return sum(self.counts.values())

LWW-Register (Last-Writer-Wins)

Simple but loses concurrent updates. Use when conflicts are rare or acceptable.

RGA (Replicated Growable Array)

For ordered sequences (e.g., collaborative text). Each element has a unique ID and tombstone for deletion.

PouchDB/CouchDB Sync

const localDB = new PouchDB('local');
const remoteDB = new PouchDB('https://sync.example.com/db');

localDB.sync(remoteDB, {
  live: true,
  retry: true,
  filter: (doc) => doc.type === 'record'
}).on('change', (info) => {
  console.log('Synced', info.docs_written, 'docs');
}).on('error', (err) => {
  console.error('Sync error', err);
});

CouchDB uses MVCC and document-level conflict detection. Conflicts surface as _conflicts array; application logic chooses winner or merges.

Merge Strategies

Last-Write-Wins (LWW)

  • Timestamp-based, simple
  • Clock skew issues
  • Use for low-conflict data

Operational Transform (OT)

  • For collaborative editing
  • Requires central coordination or CRDT

Custom Merge

  • Domain-specific logic
  • E.g., numeric max, set union, append-only logs

Implementation Considerations

Storage

  • IndexedDB for browser
  • SQLite for mobile
  • LevelDB for Node

Bandwidth

  • Delta sync (only changed docs)
  • Compression
  • Conditional requests (If-None-Match)

Testing

  • Simulate network partitions
  • Test conflict scenarios
  • Verify convergence

Conclusion

Offline-first sync with CRDTs or document-based replication enables applications that work reliably in low-connectivity environments. Choosing the right merge strategy depends on data semantics and conflict frequency.