---
title: "Min-Register (Monotone Low-Water Mark)"
url: "https://nsta1.github.io/Orleans.Lattice/docs/crdt/minregister.html"
source: "https://github.com/NSTA1/Orleans.Lattice/blob/release/9.9/docs/crdt/minregister.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"
---
# Min-Register (Monotone Low-Water Mark)

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

`tree.MinRegister<T>(key, orderKeySelector)` -> `MinRegisterAccessor<T>`, merge
mode `LatticeMergeMode.MinRegister`.

## Semantics

A **Min-Register** is the mirror image of the [Max-Register](maxregister.md): it
keeps the **smallest** totally-ordered value ever written - a monotone low-water
mark. A write advances the register only when the candidate beats the current
value under the total order; concurrent active-active writes from different
clusters converge on the single smallest value because the fold is a directional
**min** over the total order, which is commutative, associative, and idempotent.

As with the Max-Register, you supply an `orderKeySelector` that produces an
**order-preserving** `byte[]` key for a value - its unsigned lexicographic byte
order must match the intended value order. The key travels on the wire alongside
the value so the receiver folds without needing your comparer.

Reach for it when a value only ever moves down: a min-seen latency floor, a
first-seen timestamp, a lowest-price watermark. Its mirror is the
[Max-Register](maxregister.md); for keeping concurrent values rather than the
extreme, use the [MV-Register](mvregister.md).

## Behaviour

Figure: Two concurrent Min-Register writes converging on the smaller. An order diagram drawn as a chain. A Min-Register orders values downward, so a smaller value sits higher. At the bottom the register is unset. Cluster A writes 42, one step up the chain. Cluster B, at the same time, writes 37, which is smaller and so sits above 42. Merging keeps the smaller value, so A rises to 37 and B stays there.

Both clusters hold 37. Under the min order the smaller value is the higher state, so the join is the smaller write itself.

The fold keeps the smaller value under the total order, so it is commutative, associative, and idempotent.

```mermaid
sequenceDiagram
    participant A as Cluster A
    participant B as Cluster B
    A->>A: Set(42)
    B->>B: Set(37)
    A-->>B: merge ships candidate 42
    B-->>A: merge ships candidate 37
    Note over A,B: fold keeps the smaller under the total order
    Note over A,B: converged = 37 (min-wins)
```

## Example

```csharp verify
// The order key must be order-preserving: big-endian bytes of a non-negative reading.
var floor = tree.MinRegister<long>("service:api:latency-floor-ms", static v =>
{
    var key = new byte[8];
    System.Buffers.Binary.BinaryPrimitives.WriteInt64BigEndian(key, v);
    return key;
});

// Two clusters report latencies concurrently; the register keeps the smaller.
await floor.SetAsync(42, cancellationToken);
await floor.SetAsync(37, cancellationToken);

long lowest = await floor.GetAsync(cancellationToken); // 37

// Before the first write GetAsync returns default(T) - 0 for a long - so ask
// HasValueAsync when "never written" must be told apart from a real 0.
bool written = await floor.HasValueAsync(cancellationToken);
```

See also: its high-water mirror [Max-Register](maxregister.md), the multi-value
[MV-Register](mvregister.md), and the [CRDT overview](readme.md).
