---
title: "G-Counter (Grow-Only Counter)"
url: "https://nsta1.github.io/Orleans.Lattice/docs/crdt/gcounter.html"
source: "https://github.com/NSTA1/Orleans.Lattice/blob/release/9.9/docs/crdt/gcounter.md"
documents: "Orleans.Lattice 9.9.0 (release line 9.9)"
built: "2026-10-04"
all-pages: "https://nsta1.github.io/Orleans.Lattice/llms.txt"
bundle: "https://nsta1.github.io/Orleans.Lattice/docs/crdt/llms-full.txt"
---
# G-Counter (Grow-Only Counter)

Part of the [CRDTs documentation](readme.md).

`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](pncounter.md): 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

Figure: 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.

```mermaid
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

```csharp verify
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](pncounter.md) and the
[CRDT overview](readme.md).
