Migration Guide
Migrate from CRDTs to ATOMiK
CRDTs and ATOMiK both solve distributed convergence without coordination. This guide maps CRDT concepts to ATOMiK operations, shows before/after code for three common patterns, and provides a step-by-step migration checklist.
When to migrate (and when not to)
Migrate when:
- ✓Your CRDT merge is a performance bottleneck (O(n) state merges)
- ✓Causality metadata (vector clocks, dot stores) dominates bandwidth
- ✓You need undo/rollback (impossible with monotonic CRDTs)
- ✓Your data is numeric or binary (counters, flags, fingerprints)
- ✓You want machine-checked algebra properties as one technical assurance input
Stay with CRDTs when:
- ✗You need rich set semantics (OR-Set add/remove with tombstones)
- ✗Your data requires causal ordering guarantees between operations
- ✗You use collaborative text editing (CRDTs like Yjs/Automerge excel here)
- ✗Your CRDT library already meets performance requirements
Conceptual Mapping
CRDTs use a join-semilattice. ATOMiK uses an Abelian group. The algebraic structure is different, but the modeled convergence property is related -- any permutation of operations produces the same final state.
| CRDT | ATOMiK | Notes |
|---|---|---|
| CRDT State | AtomikContext | Both represent a convergent replicated value. ATOMiK separates reference state from accumulated deltas. |
| merge(a, b) | ctx.accum(delta) | CRDT merge is a join on the semilattice. ATOMiK accumulation is XOR in an Abelian group. Both are commutative. |
| CRDT Payload | Delta (XOR diff) | CRDTs transmit full state or operation. ATOMiK transmits the XOR of what changed -- typically much smaller. |
| Causality metadata | Model-dependent | Vector clocks, version vectors, and dot stores may not be needed inside a fit XOR model. Protocol-level causality remains workload-specific. |
| Conflict resolution | Algebraic convergence | CRDTs resolve via lattice join (LWW, OR-Set, etc). ATOMiK converges via Abelian-group algebra when the state model fits; application conflicts may still need product-specific handling. |
| query() | ctx.read() | Both return current value. CRDT queries the lattice state. ATOMiK reconstructs: initial_state XOR accumulator. |
Pattern 1: G-Counter / PN-Counter
CRDTs use separate increment/decrement maps per node, merged with max(). ATOMiK uses a single accumulator with self-inverse deltas.
Before: CRDT PN-Counter
# Each node maintains its own counter vector
class PNCounter:
def __init__(self, node_id, num_nodes):
self.p = [0] * num_nodes
self.n = [0] * num_nodes
self.id = node_id
def increment(self): self.p[self.id] += 1
def decrement(self): self.n[self.id] += 1
def value(self): return sum(self.p) - sum(self.n)
def merge(self, other):
# O(n) merge per node
for i in range(len(self.p)):
self.p[i] = max(self.p[i], other.p[i])
self.n[i] = max(self.n[i], other.n[i])After: ATOMiK
from atomik_core import AtomikContext
# Single context, any node can accumulate
counter = AtomikContext()
counter.load(0)
# Any node: accumulate a delta
counter.accum(delta_value)
# Read: accumulator-model O(1), no CRDT merge
current = counter.read()
# Undo: apply same delta again
counter.accum(delta_value) # self-inverse
# Sync: send a compact delta, not full state
# No vector clock inside the accumulator.
# No O(n) CRDT merge inside this path.Pattern 2: LWW-Register
Last-Writer-Wins registers require synchronized clocks or hybrid logical clocks. ATOMiK can remove timestamp dependency in the modeled register path -- convergence is algebraic, not temporal.
Before: LWW-Register
class LWWRegister:
def __init__(self):
self.value = None
self.timestamp = 0
def set(self, val, ts):
if ts > self.timestamp:
self.value = val
self.timestamp = ts
def merge(self, other):
# Requires synchronized clocks!
if other.timestamp > self.timestamp:
self.value = other.value
self.timestamp = other.timestampAfter: ATOMiK
from atomik_core import AtomikContext
reg = AtomikContext()
reg.load(initial_value)
# Update: compute delta from current state
delta = new_value ^ reg.read()
reg.accum(delta)
# No timestamps. No clock sync.
# Convergence is algebraic:
# all replicas accumulating the same
# deltas reach the same state.Pattern 3: Distributed State Sync
Multi-node state synchronization using CRDTs often requires transmitting full state or operation logs with causal metadata. In a fit XOR model, ATOMiK can transmit a compact delta and converge when replicas receive the same delta set.
Before: State-based CRDT Sync
# Node A sends full state to Node B
def sync_to_peer(local_crdt, peer):
# Serialize entire CRDT state
payload = local_crdt.serialize()
# Includes: values + vector clock
# + tombstones + node metadata
peer.send(payload) # O(state_size)
def on_receive(local_crdt, payload):
remote = CRDT.deserialize(payload)
local_crdt.merge(remote) # O(n) mergeAfter: ATOMiK Delta Sync
# Node A sends a 64-bit example delta to Node B
def sync_to_peer(ctx, peer, delta):
peer.send(delta) # 8 bytes in this representation
def on_receive(ctx, delta):
ctx.accum(delta) # accumulator update
# No full-state serialization in this model
# No vector clocks inside the accumulator
# No tombstone garbage collection in this model
# Application conflicts remain model-specific
# Order-independent for the same delta multisetPerformance Comparison
Where CRDTs incur per-node or per-operation overhead for causality tracking, ATOMiK uses a compact accumulator path in the model; timing and space claims should be validated against the chosen implementation and workload.
| Dimension | CRDTs | ATOMiK |
|---|---|---|
| Merge complexity | O(n) -- depends on state size | O(1) in the accumulator model -- one XOR for fixed-width deltas |
| Metadata overhead | Vector clocks: O(nodes), Dot stores: O(ops) | No vector clock inside the XOR accumulator; protocol causality is workload-specific |
| Sync bandwidth | Full state (state-based) or op + context (op-based) | 64-bit delta in current examples; validate the protocol envelope per workload |
| State reconstruction | O(1) if materialized, O(n) if from ops | O(1) in the accumulator model -- initial XOR accumulator |
| Undo / rollback | Not supported (lattice is monotonic) | Algebraic for fit XOR deltas -- apply the same delta again |
| Formal guarantees | SEC (Strong Eventual Consistency) | Formal proof work covers the algebra; protocol guarantees remain workload-specific |
| Implementation complexity | High -- many CRDT types, each with edge cases | Small core API; integration remains workload-specific |
Migration Checklist
Assessment
- Inventory all CRDT types in use (G-Counter, PN-Counter, OR-Set, LWW-Register, etc.)
- Identify which CRDTs rely on monotonic growth (ATOMiK supports non-monotonic via self-inverse)
- Map causality metadata dependencies -- if you need causal ordering, ATOMiK alone may not suffice
- Benchmark current merge latency and sync bandwidth as a baseline
Prototype
- Install atomik-core: pip install atomik-core
- Replace one simple CRDT (G-Counter or LWW-Register) with AtomikContext
- Validate convergence: feed the same deltas in different orders, confirm identical read()
- Measure bandwidth reduction: compare CRDT sync payload vs the current ATOMiK delta representation
Integration
- Replace CRDT merge logic with accum() calls in your replication layer
- Remove vector clock / dot store metadata from your wire protocol
- Update serialization: deltas are fixed-size integers, not structured CRDT payloads
- Implement self-inverse undo if your application needs rollback (was impossible with CRDTs)
Validation
- Run convergence tests: all replicas must reach identical state regardless of delta order
- Verify self-inverse property: double-applying any delta returns to previous state
- Load test: confirm accumulator-path performance under representative traffic
- Monitor bandwidth: measure actual reduction vs CRDT baseline
The algebraic difference
CRDTs form a join-semilattice: merge is commutative, associative, and idempotent, but not invertible. State can only grow. ATOMiK forms an Abelian group: merge (XOR) is commutative, associative, has an identity element, and every element is its own inverse. In a fit model this gives you algebraic undo, fixed-width accumulator state, and lower metadata pressure -- at the cost of lattice monotonicity. If your application needs monotonic convergence (e.g., grow-only sets), CRDTs remain the right choice.