Malachi.Cluster.Membership (malachi v0.8.13)

View Source

The pure state of SWIM-style cluster membership: who is :alive, :suspect, or :dead, with per-member incarnation numbers. No processes, timers, or network: this is the deterministic core that the failure detector and gossip transport (Malachi.Cluster.MembershipServer) drive.

Each member owns its incarnation: only that member raises its own, to refute a false suspicion. An update is {member, status, incarnation}, and merging updates is a join on the lexicographic order {incarnation, rank} where rank is alive < suspect < dead. That single rule yields the SWIM precedence:

  • a higher incarnation always wins (a fresh :alive overrides an old :suspect/:dead);
  • at equal incarnation, :suspect overrides :alive and :dead overrides both;
  • equal {incarnation, rank} changes nothing (idempotent).

Because the merge is a commutative, associative, idempotent join, applying a batch of gossiped updates converges regardless of order, what makes infection-style dissemination correct.

The one exception is an update about self: a :suspect/:dead about us is refuted by bumping our own incarnation above it and re-announcing :alive, so we never stay suspected while running. apply_update/2 returns that refutation as an effect to disseminate.

Summary

Types

Opaque k/v an admin attaches to a broker (e.g. rack, dc); disseminated with the member.

What an applied update produced: a state change or a self-refutation to disseminate.

t()

Functions

The sorted list of :alive members: the live broker set for placement and healing.

Applies one {member, status, incarnation} update under the SWIM precedence rules. Returns the new view and an effect: {:applied, update} if it changed our state (disseminate it onward), {:refute, update} if it was a false suspicion about us (announce the refutation), or :ignored.

The attributes of member (%{} if unknown or none set).

Confirms member :dead at its currently known incarnation.

The incarnation of member, or nil if unknown.

Applies a batch of updates (e.g. a received gossip), returning the new view and the list of updates that changed state (applied changes plus any self-refutation) to disseminate onward. Order-independent: the resulting view is the same for any permutation of updates.

Builds a membership view local to self (which starts :alive at incarnation 0). :peers seeds other members, also :alive at incarnation 0. :attributes are self's own attributes (peers' attributes are learned via gossip).

Sets self's attributes, raising its own incarnation so the change wins the merge everywhere. Returns the new view and {:applied, update} to disseminate (attributes are owned by their member).

The status of member, or nil if unknown.

Marks member :suspect at its currently known incarnation (a no-op if already overridden).

The full view as a list of {member, status, incarnation, attributes} updates, for gossiping.

Types

attributes()

@type attributes() :: %{optional(term()) => term()}

Opaque k/v an admin attaches to a broker (e.g. rack, dc); disseminated with the member.

effect()

@type effect() :: {:applied, update()} | {:refute, update()} | :ignored

What an applied update produced: a state change or a self-refutation to disseminate.

incarnation()

@type incarnation() :: non_neg_integer()

member()

@type member() :: term()

member_state()

@type member_state() :: %{
  status: status(),
  incarnation: incarnation(),
  attributes: attributes()
}

status()

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

t()

@type t() :: %Malachi.Cluster.Membership{
  members: %{required(member()) => member_state()},
  self: member()
}

update()

@type update() :: {member(), status(), incarnation(), attributes()}

Functions

alive_members(view)

@spec alive_members(t()) :: [member()]

The sorted list of :alive members: the live broker set for placement and healing.

apply_update(view, arg)

@spec apply_update(t(), update()) :: {t(), effect()}

Applies one {member, status, incarnation} update under the SWIM precedence rules. Returns the new view and an effect: {:applied, update} if it changed our state (disseminate it onward), {:refute, update} if it was a false suspicion about us (announce the refutation), or :ignored.

attributes(view, member)

@spec attributes(t(), member()) :: attributes()

The attributes of member (%{} if unknown or none set).

confirm(view, member)

@spec confirm(t(), member()) :: {t(), effect()}

Confirms member :dead at its currently known incarnation.

incarnation(view, member)

@spec incarnation(t(), member()) :: incarnation() | nil

The incarnation of member, or nil if unknown.

merge(view, updates)

@spec merge(t(), [update()]) :: {t(), [update()]}

Applies a batch of updates (e.g. a received gossip), returning the new view and the list of updates that changed state (applied changes plus any self-refutation) to disseminate onward. Order-independent: the resulting view is the same for any permutation of updates.

new(self, opts \\ [])

@spec new(
  member(),
  keyword()
) :: t()

Builds a membership view local to self (which starts :alive at incarnation 0). :peers seeds other members, also :alive at incarnation 0. :attributes are self's own attributes (peers' attributes are learned via gossip).

set_attributes(view, attributes)

@spec set_attributes(t(), attributes()) :: {t(), effect()}

Sets self's attributes, raising its own incarnation so the change wins the merge everywhere. Returns the new view and {:applied, update} to disseminate (attributes are owned by their member).

status(view, member)

@spec status(t(), member()) :: status() | nil

The status of member, or nil if unknown.

suspect(view, member)

@spec suspect(t(), member()) :: {t(), effect()}

Marks member :suspect at its currently known incarnation (a no-op if already overridden).

updates(view)

@spec updates(t()) :: [update()]

The full view as a list of {member, status, incarnation, attributes} updates, for gossiping.