In a distributed peer-to-peer system, multiple users can modify the same data simultaneously. GenosDB’s conflict resolution system ensures data consistency and integrity across all peers using a Last-Write-Wins (LWW) strategy enhanced by Hybrid Logical Clocks (HLC).
Conflict resolution happens automatically and transparently. Developers don’t need to manually handle conflicts in most cases.
// Peer A (offline) updates a user profileawait db.put({ name: 'Alice', status: 'busy' }, 'user123')// Peer B (offline) updates the same profileawait db.put({ name: 'Alice', status: 'available' }, 'user123')// Both peers come online and sync - which version wins?
Without a central server to serialize writes, we need a deterministic way to resolve such conflicts.
The foundation of GenosDB’s conflict resolution is the Hybrid Logical Clock, which combines physical time with logical counters to create causally-ordered timestamps.
Extract the HLC timestamp from the incoming operation.
2
Advance Physical Component
Update local clock’s physical time to the maximum of:
Current local time
Remote timestamp’s physical time
3
Update Logical Component
Adjust the logical counter to ensure the next local timestamp will be causally after the remote event.
This synchronization protocol propagates causal information through the network, ensuring all peers converge toward a consistent view of event ordering.
For advanced use cases, you can provide a custom conflict resolver:
const db = await gdb('my-app', { rtc: true, resolveConflict: (localNode, remoteNode) => { // Custom logic: merge values instead of replacing return { ...localNode.value, ...remoteNode.value, // Keep the later timestamp _timestamp: remoteNode.timestamp > localNode.timestamp ? remoteNode.timestamp : localNode.timestamp } }})
Custom conflict resolvers must maintain commutativity (order-independent results) and idempotency (same result when applied multiple times) to ensure eventual consistency.
Data Loss: LWW can discard concurrent updates. If two users edit different fields simultaneously, one user’s changes may be lost.
// Initial: { name: 'Alice', age: 30, city: 'NYC' }// User A updates age (timestamp: 1000)await db.put({ name: 'Alice', age: 31, city: 'NYC' }, 'user1')// User B updates city (timestamp: 999)await db.put({ name: 'Alice', age: 30, city: 'SF' }, 'user1')// Result: User A's entire update wins// Final: { name: 'Alice', age: 31, city: 'NYC' }// User B's city change is lost!
Solution: Design your data model with granular nodes:
// Better: Separate nodes for each attributeawait db.put({ age: 31 }, 'user1:age')await db.put({ city: 'SF' }, 'user1:city')// Now both updates can coexist
// ✅ Idempotent: Same result if applied multiple timesawait db.put({ status: 'active' }, 'user1:status')// ❌ Non-idempotent: Result depends on order and frequencyawait db.put({ count: currentCount + 1 }, 'counter')