Grove.CRDT behaviour (Grove v0.1.1)

View Source

Behaviour for implementing Conflict-free Replicated Data Types (CRDTs).

CRDTs are data structures that can be replicated across multiple nodes, modified independently and concurrently, and merged back together automatically, guaranteeing eventual consistency.

CRDT Properties

All implementations must satisfy these algebraic properties for merge/2:

  • Commutativity: merge(a, b) == merge(b, a)
  • Associativity: merge(merge(a, b), c) == merge(a, merge(b, c))
  • Idempotence: merge(a, a) == a

Example Implementation

defmodule MyApp.GCounter do
  @behaviour Grove.CRDT

  defstruct actor: nil, counts: %{}

  @impl true
  def new(actor), do: %__MODULE__{actor: actor}

  @impl true
  def merge(%__MODULE__{} = a, %__MODULE__{} = b) do
    merged = Map.merge(a.counts, b.counts, fn _k, v1, v2 -> max(v1, v2) end)
    %{a | counts: merged}
  end

  @impl true
  def value(%__MODULE__{counts: counts}) do
    Enum.sum(Map.values(counts))
  end
end

Summary

Callbacks

Applies an operation to the CRDT state.

Returns the accumulated delta since the last reset.

Merges two CRDT states into one.

Creates a new CRDT instance for the given actor.

Resets the delta accumulator.

Returns the current value of the CRDT.

Types

actor()

@type actor() :: term()

delta()

@type delta() :: term()

operation()

@type operation() :: term()

t()

@type t() :: term()

Callbacks

apply_operation(t, operation)

(optional)
@callback apply_operation(t(), operation()) :: t()

Applies an operation to the CRDT state.

Used for operation-based CRDTs (CmRDTs).

delta(t)

(optional)
@callback delta(t()) :: delta()

Returns the accumulated delta since the last reset.

Used for delta-state CRDTs to minimize bandwidth.

merge(t, t)

@callback merge(t(), t()) :: t()

Merges two CRDT states into one.

This operation must be commutative, associative, and idempotent.

new(actor)

@callback new(actor()) :: t()

Creates a new CRDT instance for the given actor.

The actor identifier should be unique across all replicas in the system.

reset_delta(t)

(optional)
@callback reset_delta(t()) :: t()

Resets the delta accumulator.

Called after the delta has been synced to other replicas.

value(t)

@callback value(t()) :: term()

Returns the current value of the CRDT.

This is the "view" that applications typically work with.