CRDTs¶
Conflict-free Replicated Data Types: GCounter, PNCounter, LWWRegister, ORSet.
Conflict-free Replicated Data Types (CRDTs) for distributed simulation.
CRDTs are data structures that converge automatically after network partitions heal, without requiring consensus. They guarantee eventual consistency by ensuring merge operations are commutative, associative, and idempotent.
Provided CRDTs:
- GCounter: Grow-only counter (increment only)
- PNCounter: Positive-negative counter (increment and decrement)
- LWWRegister: Last-writer-wins register (HLC timestamp ordering)
- ORSet: Observed-remove set (add-wins semantics)
The CRDTStore entity wraps CRDTs in a key-value store with gossip-based replication for use in simulations.
CRDTStore ¶
CRDTStore(
name: str,
network: Entity,
crdt_factory: Callable[[str], CRDT] = lambda node_id: (
LWWRegister(node_id)
),
gossip_interval: float = 1.0,
)
Bases: Entity
Key-value store backed by CRDTs with gossip replication.
Each key maps to a CRDT instance created by crdt_factory.
Writes apply directly to the local CRDT (no coordination).
Gossip periodically synchronizes state with a random peer using
full-state push-pull.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Entity name (also used as the node_id for CRDTs). |
required |
network
|
Entity
|
Network entity for inter-node communication. |
required |
crdt_factory
|
Callable[[str], CRDT]
|
Callable that creates a new CRDT given a node_id. Defaults to creating LWWRegister instances. |
lambda node_id: LWWRegister(node_id)
|
gossip_interval
|
float
|
Seconds between gossip rounds (0 to disable). |
1.0
|
convergence_lag
property
¶
True if local state hash differs from last-seen peer hash.
A rough indicator — not authoritative across all peers.
add_peers ¶
Set peer nodes for gossip replication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peers
|
list[Entity]
|
List of peer CRDTStore entities. |
required |
get_gossip_event ¶
get_or_create ¶
Get or create a CRDT for the given key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key to look up or create. |
required |
Returns:
| Type | Description |
|---|---|
CRDT
|
The CRDT instance for this key. |
handle_event ¶
handle_event(
event: Event,
) -> Generator[
float | SimFuture | tuple[float, list[Event] | Event],
None,
list[Event] | Event | None,
]
Route events by type.
CRDTStoreStats
dataclass
¶
CRDTStoreStats(
writes: int = 0,
reads: int = 0,
gossip_sent: int = 0,
gossip_received: int = 0,
keys_merged: int = 0,
convergence_checks: int = 0,
)
Statistics for a CRDTStore node.
Attributes:
| Name | Type | Description |
|---|---|---|
writes |
int
|
Local write operations processed. |
reads |
int
|
Local read operations processed. |
gossip_sent |
int
|
Gossip push messages sent. |
gossip_received |
int
|
Gossip messages received (push or response). |
keys_merged |
int
|
Total key-level merge operations performed. |
convergence_checks |
int
|
Number of convergence hash comparisons. |
GCounter ¶
Grow-only counter CRDT.
Each node has its own monotonically increasing count. The total value is the sum across all nodes. Merge uses element-wise max, which is commutative, associative, and idempotent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_id
|
str
|
Identifier for this replica. |
required |
increment ¶
Increment this node's count.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Amount to increment (must be positive). |
1
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If n is not positive. |
node_value ¶
Get a specific node's count.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_id
|
str
|
The node to query. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The node's count, or 0 if unknown. |
merge ¶
Merge another G-Counter into this one (element-wise max).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
GCounter
|
Another GCounter to merge from. |
required |
from_dict
classmethod
¶
Deserialize from a plain dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
Dict produced by |
required |
LWWRegister ¶
Last-Writer-Wins register CRDT.
Stores a single value tagged with an HLCTimestamp. On merge,
the value with the higher timestamp wins. HLCTimestamp provides
total ordering via (physical_ns, logical, node_id).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_id
|
str
|
Identifier for this replica. |
required |
value
|
Any
|
Initial value (default None). |
None
|
timestamp
|
HLCTimestamp | None
|
Initial timestamp (default None = never written). |
None
|
set ¶
Set the value if the timestamp is newer than the current one.
If the register has never been written (timestamp is None), the value is always accepted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
The new value. |
required |
timestamp
|
HLCTimestamp
|
The HLC timestamp for this write. |
required |
merge ¶
Merge another register into this one (highest timestamp wins).
If the other register has never been written, this is a no-op.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
LWWRegister
|
Another LWWRegister to merge from. |
required |
from_dict
classmethod
¶
Deserialize from a plain dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
Dict produced by |
required |
ORSet ¶
Observed-Remove Set CRDT.
Maintains a dict mapping elements to sets of tags. Each tag is a
(node_id, sequence_number) tuple, generated deterministically
(no UUIDs) for reproducible tests.
Add-wins semantics: a concurrent add and remove of the same element results in the element being present (the new tag from the add survives the remove of old tags).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_id
|
str
|
Identifier for this replica. |
required |
add ¶
Add an element with a new unique tag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
element
|
Any
|
The element to add. |
required |
remove ¶
Remove an element by clearing all its observed tags.
If the element is not present, this is a no-op.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
element
|
Any
|
The element to remove. |
required |
contains ¶
Check if an element is in the set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
element
|
Any
|
The element to check. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the element has at least one tag. |
merge ¶
Merge another OR-Set into this one.
For each element, the resulting tag set is the union of both replicas' tags. This means: - Elements added on either side are present. - An element removed on one side but concurrently added on the other survives (add-wins).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
ORSet
|
Another ORSet to merge from. |
required |
from_dict
classmethod
¶
Deserialize from a plain dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
Dict produced by |
required |
PNCounter ¶
Positive-Negative counter CRDT.
Wraps two G-Counters: _p for positive increments and _n
for negative decrements. The value is _p.value - _n.value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_id
|
str
|
Identifier for this replica. |
required |
increment ¶
Increment the counter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Amount to increment (must be positive). |
1
|
decrement ¶
Decrement the counter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Amount to decrement (must be positive). |
1
|
merge ¶
Merge another PN-Counter into this one.
Merges both the P and N G-Counters independently.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
PNCounter
|
Another PNCounter to merge from. |
required |
from_dict
classmethod
¶
Deserialize from a plain dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
Dict produced by |
required |
CRDT ¶
Bases: Protocol
Protocol for all CRDT types.
All CRDTs must support:
- value: Read the current resolved value.
- merge(other): Merge another replica's state (in-place).
- to_dict() / from_dict(): Serialization for gossip.
merge ¶
Merge another replica's state into this one (in-place).
Must be commutative, associative, and idempotent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Self
|
Another instance of the same CRDT type. |
required |
from_dict
classmethod
¶
Deserialize a CRDT from a plain dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
Dict produced by |
required |