lattice / orientation
Quick Start
Get up and running with lattice in minutes.
Install the umbrella package:
gleam add lattice_crdtThis gives you the core CRDT packages. See Installation for individual package options, including distributed presence.
Merge counters from multiple replicas
Section titled “Merge counters from multiple replicas”GCounter is the simplest place to start: each replica can only increase its
own contribution, and merging takes the per-replica maximum.
Save this as src/quick_start.gleam:
On narrow screens, scroll code blocks horizontally to read each full line.
import gleam/intimport gleam/ioimport lattice_core/replica_idimport lattice_counters/g_counter
pub fn main() { let assert Ok(counter_a) = g_counter.new(replica_id.new("node-a")) |> g_counter.increment(2)
let assert Ok(counter_b) = g_counter.new(replica_id.new("node-b")) |> g_counter.increment(3)
let merged = g_counter.merge(counter_a, counter_b)
merged |> g_counter.value |> int.to_string |> io.println}Run the example from your project directory:
gleam run -m quick_startExpected output:
5Success: you now have a convergent counter: two replicas updated independently, then merged into the same combined value.
g_counter.increment returns Result and rejects negative deltas with
Error(NegativeDelta(delta)). If you need a counter that can
go down, use pn_counter from lattice_counters; both increment and
decrement still take non-negative deltas because the underlying state is
grow-only.
For incremental replication, counters also expose *_with_delta mutators that
return Ok(#(new_state, delta)) or an error. See
Delta-State Replication.
Advanced recipes
Section titled “Advanced recipes”Open these when you are ready to move beyond the first convergent counter. Each recipe has a stable fragment link: LWW register and OR-Map.
Advanced recipeResolve ties deterministically with LWW registersChoose a value by timestamp, with replica IDs breaking equal-timestamp ties.
LWWRegister stores a value plus a timestamp. Newer timestamps win. If two
replicas write at the same timestamp, lattice breaks the tie deterministically
using the replica ID.
import lattice_core/replica_idimport lattice_registers/lww_register
pub fn main() { let left = lww_register.new("apple", 7, replica_id.new("node-a")) let right = lww_register.new("zebra", 7, replica_id.new("node-b"))
let merged = lww_register.merge(left, right)
lww_register.value(merged) // -> "zebra" (node-b > node-a lexicographically)}lww_register.set only applies when the new timestamp is strictly greater than
the current one.
Advanced recipeStore CRDT values inside an OR-MapTrack add-wins keys while merging a nested CRDT value for each key.
ORMap(a) tracks keys with add-wins semantics within a generation and
stores a CRDT child at each key. CrdtSpec(a) determines its initial
value. Re-adding a removed key starts a fresh generation; the newer
generation wins over older content.
import lattice_core/replica_idimport lattice_counters/g_counterimport lattice_maps/crdtimport lattice_maps/or_map
fn add_points(value: crdt.Crdt(String), delta: Int) -> crdt.Crdt(String) { case value { crdt.CrdtGCounter(counter) -> { let assert Ok(counter) = g_counter.increment(counter, delta) crdt.CrdtGCounter(counter) }
other -> other }}
pub fn main() { let assert Ok(scoreboard_a) = or_map.new(replica_id.new("node-a"), crdt.GCounterSpec) |> or_map.update("alice", fn(value) { add_points(value, 2) })
let assert Ok(scoreboard_b) = or_map.new(replica_id.new("node-b"), crdt.GCounterSpec) |> or_map.update("alice", fn(value) { add_points(value, 3) })
let assert Ok(merged) = or_map.merge(scoreboard_a, scoreboard_b)
case or_map.get(merged, "alice") { Ok(crdt.CrdtGCounter(counter)) -> g_counter.value(counter) _ -> 0 } // -> 5}or_map.update returns Error(TypeMismatch(...)) if the callback returns a
different CRDT variant from the map's specification. The examples use known
positive amounts; handle counter errors before updating a map when amounts
come from external input.
For more detail, see the Counters guide, Registers guide, Sets guide, and Maps guide. For topic/key presence tracking, see the Presence guide.
