lattice/g_counter

A grow-only counter (G-Counter) CRDT.

Each replica maintains its own monotonically increasing count. The global value is the sum across all replicas. Merge takes the pairwise maximum of each replica’s count, guaranteeing convergence.

G-Counter is non-opaque because pn_counter directly accesses its internal fields for serialization. Adding opaque accessor functions would not improve the API for this paired type.

Example

import lattice/g_counter

let a = g_counter.new("node-a") |> g_counter.increment(3)
let b = g_counter.new("node-b") |> g_counter.increment(5)
let merged = g_counter.merge(a, b)
g_counter.value(merged)  // -> 8

Types

A grow-only counter that tracks per-replica counts.

Each replica identified by a String ID maintains its own count. The global value is the sum of all per-replica counts. The type is non-opaque so that pn_counter can access fields for serialization; do not rely on internal field names in application code.

pub type GCounter {
  GCounter(dict: dict.Dict(String, Int), self_id: String)
}

Constructors

  • GCounter(dict: dict.Dict(String, Int), self_id: String)

Values

pub fn from_json(
  json_string: String,
) -> Result(GCounter, json.DecodeError)

Decode a G-Counter from a JSON string produced by to_json.

Returns Ok(GCounter) on success, or Error(json.DecodeError) if the input is not a valid G-Counter JSON envelope.

pub fn increment(counter: GCounter, delta: Int) -> GCounter

Increment the counter by delta.

Adds delta to this replica’s count. delta should be a non-negative integer; passing a negative value will decrease the local count, which violates the grow-only invariant and may cause incorrect merge results.

pub fn merge(a: GCounter, b: GCounter) -> GCounter

Merge two G-Counters using pairwise maximum.

For each replica, the merged count is the maximum of the two inputs. The result’s self_id is taken from a.

This operation is commutative, associative, and idempotent, satisfying the CRDT join-semilattice laws. Any ordering of concurrent merges will produce the same final state.

pub fn new(replica_id: String) -> GCounter

Create a new G-Counter for the given replica.

Returns a fresh counter where all per-replica counts are zero. The replica_id identifies this node and is used when incrementing.

pub fn to_json(counter: GCounter) -> json.Json

Encode a G-Counter as a self-describing JSON value.

Produces an envelope with type, v (schema version), and state. Format: {"type": "g_counter", "v": 1, "state": {"self_id": "...", "counts": {...}}}

Use from_json to decode the result back into a GCounter.

pub fn value(counter: GCounter) -> Int

Get the current value of the counter.

Returns the sum of all per-replica counts, which represents the total number of increments applied across all replicas observed by this counter.

Search Document