DSON: Delta-State CRDTs Revolutionize Resilient Data Sync for Edge and P2P Networks
Share this article
In today’s fragmented digital landscape, applications operating in peer-to-peer (P2P) or edge networks—think remote field sensors, collaborative mobile apps, or decentralized services—face a brutal reality: spotty connectivity, high latency, and unpredictable bandwidth. Traditional data synchronization methods crumble under these conditions, leading to conflicts, data loss, or crippling inefficiencies. Enter DSON, a delta-state Conflict-Free Replicated Datatype (CRDT) specifically engineered for JSON-like structures, now available as an open-source Rust crate. Helsing’s release of dson isn’t just another library; it’s a foundational toolkit for building applications that thrive in the most hostile network environments.
Why CRDTs? The CAP Theorem Trade-Off
CRDTs are the unsung heroes of AP (Available + Partition-Tolerant) systems under the CAP Theorem. When networks partition—as they inevitably do in edge scenarios—CRDTs ensure applications remain operational for both reads and writes, sacrificing strong consistency for availability. Imagine two users editing a shared shopping list offline: Alice removes "milk" while Bob adds "ice cream." Upon reconnecting, a CRDT merges these changes automatically without conflicts. This Strong Eventual Consistency (SEC) guarantee is powerful, but implementations vary wildly:
- Operation-based CRDTs transmit small, targeted operations (e.g., "insert at index 5"). They demand reliable, ordered messaging and fail catastrophically in chaotic networks.
- State-based CRDTs exchange entire states, ensuring robustness but exploding bandwidth costs.
Delta-state CRDTs, like DSON, strike a middle path. They transmit compact, self-contained "deltas" representing changes, which can be merged like full states. This slashes message size while tolerating packet loss, duplication, or delays—perfect for opportunistic P2P sync.
DSON’s Breakthrough: Tombstone-Free and Causal
DSON, based on academic research, introduces two key innovations for JSON documents:
Observed-Remove (OR) Semantics: Elements are only removed if the remover has seen their addition. Concurrent updates trump deletions—preserving user intent. For example, if Alice updates "bananas" to "blueberries" while Bob deletes "bananas," Alice’s edit wins.
Causal Contexts, No Tombstones: Unlike many CRDTs, DSON avoids tombstones (persistent deletion markers) by tracking operations via "dots"—tuples of replica ID and sequence number. Deletions are inferred from missing dots in the causal history, ensuring metadata scales with live data, not historical operations. This eliminates unbounded growth in long-running systems.
Inside the dson Rust Crate: Extensible and Low-Level
Helsing’s implementation provides composable primitives without forcing networking opinions:
- Core Types:
OrMap<K, V>(observed-remove map),OrArray<V>(array), andMvReg<T>(multi-value register for conflicts). - Extensibility: The
ExtensionTypetrait lets developers embed custom CRDTs (e.g., counters or rich-text) within DSON structures.
Here’s a minimal example showing concurrent writes and automatic conflict resolution:
use dson::{
crdts::{mvreg::MvRegValue, snapshot::ToValue, NoExtensionTypes},
CausalDotStore, Identifier, OrMap,
};
// Setup replicas for Alice and Bob
let alice_id = Identifier::new(0, 0);
let mut alice_store = CausalDotStore::<OrMap<String>>::default();
let bob_id = Identifier::new(1, 0);
let mut bob_store = CausalDotStore::<OrMap<String>>::default();
// Alice writes "initial" to a key
let key = "shared_key".to_string();
let delta_from_alice = dson::api::map::apply_to_register::<_, NoExtensionTypes, _>(
|reg, ctx, id| reg.write("initial".to_string().into(), ctx, id),
key.clone(),
)(&alice_store.store, &alice_store.context, alice_id);
alice_store.join_or_replace_with(delta_from_alice.store.clone(), &delta_from_alice.context);
// Bob syncs and both edit concurrently
bob_store.join_or_replace_with(delta_from_alice.store, &delta_from_alice.context);
let delta_alice_edit = /* Alice writes "from Alice" */;
alice_store.join_or_replace_with(delta_alice_edit.store.clone(), &delta_alice_edit.context);
let delta_bob_edit = /* Bob writes "from Bob" */;
bob_store.join_or_replace_with(delta_bob_edit.store.clone(), &delta_bob_edit.context);
// After syncing deltas, state converges with both values preserved
alice_store.join_or_replace_with(delta_bob_edit.store, &delta_bob_edit.context);
bob_store.join_or_replace_with(delta_alice_edit.store, &delta_alice_edit.context);
let register_value = alice_store.store.get(&key).unwrap();
let values: Vec<_> = register_value.reg.values().into_iter().collect();
assert!(values.contains(&&MvRegValue::String("from Alice".to_string())));
assert!(values.contains(&&MvRegValue::String("from Bob".to_string())));
As seen, conflicts are exposed deterministically, allowing application-specific resolution—crucial for nuanced user experiences.
The Road Ahead: Why This Matters
For developers building collaborative tools, IoT networks, or offline-capable apps, dson offers a battle-tested foundation. Its delta-state design reduces sync overhead by up to 90% compared to state-based CRDTs, while Rust’s performance and safety make it ideal for resource-constrained edges. Helsing plans to enhance extensibility and delta decomposition, but the real potential lies in community adoption. As one engineer noted, "In a world of flaky connections, CRDTs like DSON aren’t just convenient—they’re existential." Explore the crate on GitHub, contribute, and start syncing fearlessly.
Source: Helsing Blog