CRDT stands for Conflict-free Replicated Data Type - a data structure that can be replicated across multiple peers and automatically resolves conflicts without coordination.GUN uses CRDTs to ensure that when multiple peers modify data simultaneously (especially while offline), they can eventually converge to the same state without manual conflict resolution.
// Two peers write simultaneouslygunA.get('counter').put(5); // Peer AgunB.get('counter').put(7); // Peer B at the same time// No conflict! Both peers eventually converge to: 7// The write with the higher timestamp wins
CRDTs enable strong eventual consistency - all replicas that have received the same updates will have the same state.
// Alice edits from her phone (offline)gun.get('doc').put({title: 'Alice version'});// Bob edits from his laptop (offline) gun.get('doc').put({title: 'Bob version'});// Both come online - now what?// Without CRDTs: ❌ Conflict error, manual resolution needed// With CRDTs: ✅ Automatic resolution, no intervention needed
GUN uses Last-Write-Wins (LWW) semantics based on state vectors:
// Peer A writes at time 1000gunA.get('x').put('A');// State: {x: 1000}// Peer B writes at time 1001gunB.get('x').put('B');// State: {x: 1001}// When peers sync:// 1001 > 1000, so 'B' wins// Both converge to: 'B'
From src/state.js:5-9, GUN generates sub-millisecond timestamps:
var N = 0, D = 999;function State(){ var t = +new Date; if(last < t){ return N = 0, last = t + State.drift; } return last = t + ((N += 1) / D) + State.drift;}
This allows multiple writes per millisecond with deterministic ordering:
// Millisecond 1234567890:State(); // Returns: 1234567890.000State(); // Returns: 1234567890.001State(); // Returns: 1234567890.002// ... up to 999 writes per millisecond// Next millisecond 1234567891:State(); // Returns: 1234567891.000
GUN can handle 999 writes per millisecond on a single peer without timestamp collisions.
// All peers eventually converge to same stategunA.get('x').put('A');gunB.get('x').put('B');// After sync, both have same valuegunA.get('x').once(x => console.log(x)); // 'B'gunB.get('x').once(x => console.log(x)); // 'B'
// Both writes valid, but one is lostgunA.get('counter').put(5);gunB.get('counter').put(7);// Result: 7 (not 12!)// The write with higher timestamp wins, other is discarded
For counters, use custom CRDT logic or operational transforms. LWW-Register may lose data.
// Peer A:gun.get('x').put('A'); // Time: 1000gun.get('y').put('B'); // Time: 1001, depends on x// Peer B might receive out of order:// y=B (1001) arrives before x=A (1000)// Causality not preserved
For causal consistency, use version vectors (not implemented in GUN core).
// GUN's .set() is already a grow-only set CRDTgun.get('tags').set('javascript');gun.get('tags').set('database');gun.get('tags').set('p2p');// Elements can be added but not removed (grow-only)// To remove, use tombstonesgun.get('tags').get(elementId).put(null);