SwimEx.Membership (SwimEx v0.1.0)

View Source

Pure functional membership state machine.

Tracks cluster members and their states. All functions return a new state; no side effects.

Incarnation rules (SWIM+Susp)

  • alive(node, inc): accepted if inc > current_inc, OR if node is unknown (new join). Dead nodes revived only if inc > dead_inc (restart with time-based incarnation ensures this).
  • suspect(node, inc): accepted if inc >= current_inc and node is alive or suspect.
  • dead(node, inc): accepted if inc >= current_inc and node is not already dead.

Summary

Functions

Adds a new node to the membership state with the given incarnation.

Applies a gossip event to the membership state.

Garbage collects dead members that have been dead for longer than expiry_ms.

Returns the membership entry for a node, or nil if not found.

Lists members in the cluster.

Returns the count of non-dead members (alive or suspect).

Returns a new, empty membership state.

Marks a node as alive with the given incarnation.

Types

member()

@type member() :: %{
  status: status(),
  incarnation: non_neg_integer(),
  dead_at: integer() | nil
}

node_id()

@type node_id() :: {String.t(), :inet.port_number(), String.t()}

status()

@type status() :: :alive | :suspect | :dead

t()

@type t() :: %SwimEx.Membership{members: %{required(node_id()) => member()}}

Functions

add(state, node, incarnation)

@spec add(t(), node_id(), non_neg_integer()) :: t()

Adds a new node to the membership state with the given incarnation.

Returns the updated membership state.

apply_event(state, arg)

@spec apply_event(t(), SwimEx.Codec.event()) :: t()

Applies a gossip event to the membership state.

Follows SWIM+Suspension rules for state transitions and incarnation numbers. Returns the updated membership state.

gc(state, expiry_ms)

@spec gc(t(), non_neg_integer()) :: t()

Garbage collects dead members that have been dead for longer than expiry_ms.

Returns the updated membership state.

get(state, node)

@spec get(t(), node_id()) :: member() | nil

Returns the membership entry for a node, or nil if not found.

list(state, opts \\ [])

@spec list(
  t(),
  keyword()
) :: [
  {String.t(), :inet.port_number(), String.t(), status(), non_neg_integer()}
]

Lists members in the cluster.

Options

  • :include_dead: Boolean. If true, includes nodes marked as dead. Defaults to false.

Returns

  • A list of member tuples: {host, port, cookie, status, incarnation}.

member_count(state)

@spec member_count(t()) :: non_neg_integer()

Returns the count of non-dead members (alive or suspect).

new()

@spec new() :: t()

Returns a new, empty membership state.

set_alive(state, node, incarnation)

@spec set_alive(t(), node_id(), non_neg_integer()) :: t()

Marks a node as alive with the given incarnation.

Returns the updated membership state.