Table of Contents

G-Counter (Grow-Only Counter)

This page is part of the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at gcounter.md, and llms.txt lists every page.

tree.GCounter(key) -> GCounterAccessor, merge mode LatticeMergeMode.GCounter.

Semantics

A G-Counter is a counter that only ever goes up. Each replica keeps its own per-replica component and increments only its own; the counter's value is the sum of every replica's component. Because a replica only advances its own component and merge takes the per-replica maximum, concurrent increments from different clusters all survive - nothing is lost and nothing is double-counted on re-delivery.

It is the grow-only half of the PN-Counter: a PN-Counter is two G-Counters (one for increments, one for decrements). Reach for a G-Counter when the quantity can never decrease - page views, total bytes ingested, monotonic event tallies - and you want the smallest, tombstone-free counter primitive.

IncrementAsync takes a replicaId naming the writer and a non-negative amount; each side advances only its own component.

Behaviour

Two concurrent G-Counter increments meeting at their join. An order diagram shaped like a diamond. At the bottom is the shared starting state, A=0 and B=0, value 0. Cluster A adds 3 to its own component, reaching A=3, B=0, value 3. Cluster B, at the same time, adds 5 to its own, reaching A=0, B=5, value 5. Neither of those states is above the other. Merging takes the larger value for each replica, so both paths rise to the same top state, A=3, B=5, value 8.

Both clusters hold A=3, B=5, value 8. Either order of delivery reaches the same join.

Merge takes the larger value per replica, so it is commutative, associative, and idempotent.

sequenceDiagram
    participant A as Cluster A
    participant B as Cluster B
    A->>A: Increment("A", 3) -> A=3
    B->>B: Increment("B", 5) -> B=5
    A-->>B: merge ships component A=3
    B-->>A: merge ships component B=5
    Note over A,B: merge takes per-replica max, value = sum
    Note over A,B: converged value = 3 + 5 = 8

Example

var views = tree.GCounter("post:42:views");

// Two clusters count views concurrently; each advances its own component.
await views.IncrementAsync("cluster-A", 3, cancellationToken);
await views.IncrementAsync("cluster-B", 5, cancellationToken);

// The value is the sum across replicas - no increment is lost to a concurrent one.
long total = await views.ValueAsync(cancellationToken);

See also: the positive-negative PN-Counter and the CRDT overview.