macula_crdt (macula v0.20.5)

View Source

Conflict-Free Replicated Data Types (CRDTs) for shared state.

Implements CRDTs for eventually-consistent distributed state management: - LWW-Register (Last-Write-Wins Register) - single value with timestamp - OR-Set (Observed-Remove Set) - set with add/remove semantics - G-Counter (Grow-only Counter) - monotonically increasing counter - PN-Counter (Positive-Negative Counter) - increment/decrement counter

These CRDTs replace Ra/Raft consensus for Macula's masterless architecture. State is synchronized via gossip protocol between nodes.

Summary

Functions

Increment the G-Counter by 1 for current node

Increment the G-Counter by N for current node

Merge two G-Counters (take max of each node's count)

Get the total value of the G-Counter

Get the current value of the LWW-Register

Merge two LWW-Registers Keeps the value with the highest timestamp Ties broken by node name (lexicographic order)

Set a value in the LWW-Register with timestamp

Get the timestamp of the LWW-Register

Get the value of the LWW-Register (alias for lww_get)

Create a new G-Counter

Create a new empty LWW-Register

Create a new LWW-Register with initial value

Create a new empty OR-Set

Create a new PN-Counter

Add an element to the OR-Set (auto-generates unique tag)

Add an element to the OR-Set with a specific tag

Check if element is in the OR-Set

Get all elements in the OR-Set

Merge two OR-Sets Union of elements, union of tombstones

Remove an element from the OR-Set Removes all current tags for that element (add-wins semantics)

Get the number of elements in the OR-Set

Decrement the PN-Counter by 1

Decrement the PN-Counter by N

Increment the PN-Counter by 1

Increment the PN-Counter by N

Merge two PN-Counters

Get the value of the PN-Counter (positive - negative)

Types

gcounter/0

-type gcounter() :: #{node() => non_neg_integer()}.

lww_register/0

-type lww_register() :: #{value => term(), timestamp => timestamp(), node => node()}.

or_set/0

-type or_set() ::
          #{elements => #{term() => sets:set(unique_tag())}, tombstones => sets:set(unique_tag())}.

pncounter/0

-type pncounter() :: #{p => gcounter(), n => gcounter()}.

timestamp/0

-type timestamp() :: non_neg_integer().

unique_tag/0

-type unique_tag() :: binary().

Functions

gcounter_increment(Counter)

-spec gcounter_increment(gcounter()) -> gcounter().

Increment the G-Counter by 1 for current node

gcounter_increment(Counter, N)

-spec gcounter_increment(gcounter(), pos_integer()) -> gcounter().

Increment the G-Counter by N for current node

gcounter_merge(C1, C2)

-spec gcounter_merge(gcounter(), gcounter()) -> gcounter().

Merge two G-Counters (take max of each node's count)

gcounter_value(Counter)

-spec gcounter_value(gcounter()) -> non_neg_integer().

Get the total value of the G-Counter

lww_get(_)

-spec lww_get(lww_register()) -> term().

Get the current value of the LWW-Register

lww_merge(R1, R2)

-spec lww_merge(lww_register(), lww_register()) -> lww_register().

Merge two LWW-Registers Keeps the value with the highest timestamp Ties broken by node name (lexicographic order)

lww_set(Register, Value, Timestamp)

-spec lww_set(lww_register(), term(), timestamp()) -> lww_register().

Set a value in the LWW-Register with timestamp

lww_timestamp(_)

-spec lww_timestamp(lww_register()) -> timestamp().

Get the timestamp of the LWW-Register

lww_value(Register)

-spec lww_value(lww_register()) -> term().

Get the value of the LWW-Register (alias for lww_get)

new_gcounter()

-spec new_gcounter() -> gcounter().

Create a new G-Counter

new_lww_register()

-spec new_lww_register() -> lww_register().

Create a new empty LWW-Register

new_lww_register(Value)

-spec new_lww_register(term()) -> lww_register().

Create a new LWW-Register with initial value

new_or_set()

-spec new_or_set() -> or_set().

Create a new empty OR-Set

new_pncounter()

-spec new_pncounter() -> pncounter().

Create a new PN-Counter

or_add(Set, Element)

-spec or_add(or_set(), term()) -> or_set().

Add an element to the OR-Set (auto-generates unique tag)

or_add(_, Element, Tag)

-spec or_add(or_set(), term(), unique_tag()) -> or_set().

Add an element to the OR-Set with a specific tag

or_contains(_, Element)

-spec or_contains(or_set(), term()) -> boolean().

Check if element is in the OR-Set

or_elements(_)

-spec or_elements(or_set()) -> [term()].

Get all elements in the OR-Set

or_merge(_, _)

-spec or_merge(or_set(), or_set()) -> or_set().

Merge two OR-Sets Union of elements, union of tombstones

or_remove(Set, Element)

-spec or_remove(or_set(), term()) -> or_set().

Remove an element from the OR-Set Removes all current tags for that element (add-wins semantics)

or_size(Set)

-spec or_size(or_set()) -> non_neg_integer().

Get the number of elements in the OR-Set

pncounter_decrement(Counter)

-spec pncounter_decrement(pncounter()) -> pncounter().

Decrement the PN-Counter by 1

pncounter_decrement(_, Amount)

-spec pncounter_decrement(pncounter(), pos_integer()) -> pncounter().

Decrement the PN-Counter by N

pncounter_increment(Counter)

-spec pncounter_increment(pncounter()) -> pncounter().

Increment the PN-Counter by 1

pncounter_increment(_, Amount)

-spec pncounter_increment(pncounter(), pos_integer()) -> pncounter().

Increment the PN-Counter by N

pncounter_merge(_, _)

-spec pncounter_merge(pncounter(), pncounter()) -> pncounter().

Merge two PN-Counters

pncounter_value(_)

-spec pncounter_value(pncounter()) -> integer().

Get the value of the PN-Counter (positive - negative)