Crdt.Counter

crdt · API reference

PN-Counter CRDT extension built on vec node

Counter extension (cnt) - PN-Counter CRDT.

A PN-Counter (Positive-Negative Counter) is a CRDT that supports both increment and decrement operations. It's built on a vec node where:

  • Even slots (0, 2, 4, ...) store positive counts per session
  • Odd slots (1, 3, 5, ...) store negative counts per session Each session has two dedicated slots:
  • Slot 2*(session_id mod 128): positive count
  • Slot 2*(session_id mod 128) + 1: negative count The counter value is: sum(all positive slots) - sum(all negative slots)

This design ensures:

  • Convergence: all replicas eventually agree on the value
  • Commutativity: operations can be applied in any order
  • Concurrent increments/decrements are preserved Usage:
  let model = Model.create session_id in
  let counter = Counter.create model in
  Counter.increment counter;
  Counter.increment ~by:5 counter;
  Counter.decrement counter;
  let v = Counter.value counter in  (* 5 *)

Types

type t = {
  model : Model.t;
  vec_node : Node.t;
}

Counter handle wrapping a vec node

Session Slot Mapping

val positive_slot : int -> int

Get the slot index for a session's positive count. Uses modular arithmetic to map sessions to slots 0-254 (even).

val negative_slot : int -> int

Get the slot index for a session's negative count. Uses slots 1-255 (odd).

Creation

val create : Model.t -> t

Create a new counter. Creates a vec node in the model.

val of_node : Model.t -> Node.t -> t

Create a counter from an existing vec node. Use this when loading a counter from a decoded model.

val id : t -> Clock.timestamp

Get the vec node's timestamp ID

Reading

val get_slot_value : t -> int -> int

Get the value stored in a slot, or 0 if not set

val value : t -> int

Calculate the current counter value.

Sums all positive slots and subtracts all negative slots.

Modification

val set_slot : t -> int -> int -> unit

Set a slot to a specific value, creating the con node

val increment : ?by:int -> t -> unit

Increment the counter by the given amount (default 1).

Uses the local session's positive slot.

val decrement : ?by:int -> t -> unit

Decrement the counter by the given amount (default 1).

Uses the local session's negative slot.

val add : t -> int -> unit

Add to the counter (positive or negative).

Positive amounts use increment, negative use decrement.

val reset_local : t -> unit

Reset the counter for the local session.

Sets both positive and negative slots to 0. Does not affect other sessions' contributions.

Inspection

val slots : t -> (int * int) list

Get the raw slot values as (slot_idx, value) pairs. Useful for debugging and serialization inspection.

val totals : t -> int * int

Get positive and negative totals separately. Returns (positive_total, negative_total).

val session_count : t -> int

Get the number of sessions that have contributed to this counter

Merging

val merge : t -> Node.t -> unit

Merge another counter's vec node into this counter.

This applies the other counter's slots to this one using CRDT semantics (last-writer-wins per slot). Used during sync.