CRDT Types Guide

Choose the right conflict-free replicated data type for your use case

What are CRDTs?

Conflict-free Replicated Data Types (CRDTs) are data structures that can be replicated across multiple nodes in a distributed system and merged automatically without conflicts. They guarantee that all replicas will eventually converge to the same state, regardless of the order of operations or network partitions.

πŸ”„

Convergence

All replicas eventually reach the same state

🚫

Conflict-Free

No manual conflict resolution required

πŸ“‘

Network Partition Tolerant

Works even when nodes are disconnected

Available CRDT Types

πŸ”’

Counters

GCounter (Grow-Only Counter)

A counter that can only be incremented. Perfect for tracking metrics that only increase.

Use Cases:
  • β€’ Event counting (page views, API calls)
  • β€’ Metrics aggregation
  • β€’ Resource usage tracking
let mut counter = GCounter::<DefaultConfig>::new(1);
counter.increment()?;
println!("Value: {}", counter.value()); // 1

PNCounter (Increment/Decrement Counter)

A counter that supports both increment and decrement operations using two internal GCounters.

Use Cases:
  • β€’ Inventory management
  • β€’ Vote counting (upvotes/downvotes)
  • β€’ Resource allocation
let mut counter = PNCounter::<DefaultConfig>::new(1);
counter.increment()?;
counter.decrement()?;
println!("Value: {}", counter.value()); // 0
πŸ“

Registers

LWWRegister (Last-Writer-Wins)

Stores a single value with timestamp-based conflict resolution. The most recent write wins.

Use Cases:
  • β€’ Configuration management
  • β€’ User profile data
  • β€’ System status tracking
let mut reg = LWWRegister::<&str, DefaultConfig>::new(1);
reg.set("value", 1000)?;
println!("Value: {:?}", reg.get()); // Some("value")

MVRegister (Multi-Value Register)

Stores multiple concurrent values when conflicts occur, allowing application-level resolution.

Use Cases:
  • β€’ Collaborative editing
  • β€’ Sensor calibration
  • β€’ Conflict detection systems
let mut reg = MVRegister::<f32, DefaultConfig>::new(1);
reg.set(1.05, 1000)?;
let values: Vec<f32> = reg.values().cloned().collect();
πŸ“¦

Sets

GSet (Grow-Only Set)

A set that only supports adding elements. Once added, elements cannot be removed.

Use Cases:
  • β€’ Device capability registry
  • β€’ Feature flags
  • β€’ Permanent audit logs
let mut set = GSet::<u32, DefaultConfig>::new();
set.insert(42)?;
println!("Contains 42: {}", set.contains(&42)); // true

ORSet (Observed-Remove Set)

A set that supports both adding and removing elements using unique tags for each operation.

Use Cases:
  • β€’ Shopping cart management
  • β€’ Active user sessions
  • β€’ Dynamic group membership
let mut set = ORSet::<u32, DefaultConfig>::new(1);
let tag = set.insert(42)?;
set.remove(&42, tag)?;
πŸ—ΊοΈ

Maps

LWWMap (Last-Writer-Wins Map)

A key-value map where each key uses last-writer-wins semantics for conflict resolution. Supports insert, update, remove, and query operations.

Use Cases:
  • β€’ Distributed configuration stores
  • β€’ Sensor data aggregation
  • β€’ User preference management
  • β€’ Metadata storage
  • β€’ Device state management
Key Operations:
insert(key, value, timestamp)

Add or update a key-value pair

remove(key)

Remove a key and return its value

get(key)

Retrieve the value for a key

merge(other)

Merge with another LWWMap

Basic Usage:
use crdtosphere::prelude::*;

let mut map = LWWMap::<&str, f32, DefaultConfig>::new(1);

// Insert values with timestamps
map.insert("temperature", 25.5, 1000)?;
map.insert("humidity", 60.0, 1001)?;

// Query values
println!("Temp: {:?}", map.get(&"temperature"));
println!("Map length: {}", map.len());
Remove Operations:
// Remove a key and get its value
let removed_value = map.remove(&"humidity");
println!("Removed: {:?}", removed_value); // Some(60.0)

// Check if key still exists
println!("Contains humidity: {}", map.contains_key(&"humidity")); // false

// Capacity is freed for new entries
println!("Remaining capacity: {}", map.remaining_capacity());
Conflict Resolution:
let mut map1 = LWWMap::<&str, i32, DefaultConfig>::new(1);
let mut map2 = LWWMap::<&str, i32, DefaultConfig>::new(2);

// Both nodes update the same key
map1.insert("counter", 10, 1000)?;
map2.insert("counter", 20, 2000)?; // Newer timestamp

// Merge maps - newer timestamp wins
map1.merge(&map2)?;
println!("Final value: {:?}", map1.get(&"counter")); // Some(20)
Atomic Version (Thread-Safe):
// Enable hardware-atomic feature for thread-safe operations - std is for demonstration.
use std::sync::Arc;
use std::thread;

let map = Arc::new(LWWMap::<&str, i32, DefaultConfig>::new(1));

// Share map between threads
let map_clone = Arc::clone(&map);
let handle = thread::spawn(move || {
    map_clone.insert("shared_data", 42, 1000)
});

handle.join().unwrap();

CRDT Selection Guide

Choose Based on Operations

Need to count things?

Use GCounter for increment-only or PNCounter for increment/decrement

Need to store single values?

Use LWWRegister for simple cases or MVRegister for conflict detection

Need to manage collections?

Use GSet for add-only or ORSet for add/remove operations

Need key-value storage?

Use LWWMap for distributed key-value data

Choose Based on Use Case

πŸš— Automotive

LWWRegister for sensor data, GCounter for error counts

πŸ€– Robotics

GSet for robot discovery, LWWMap for position tracking

🌐 IoT

LWWMap for sensor readings, GSet for device capabilities

🏭 Industrial

GCounter for production counts, LWWRegister for equipment status

Next Steps

πŸ’»

Try Examples

See practical examples of each CRDT type in action.

View Examples β†’
🌐

Domain Applications

Explore specialized CRDTs for different industries.

Explore Domains β†’