Packages

macula

0.7.3
7.1.0 7.0.0 6.0.0 5.2.2 5.2.1 5.2.0 5.1.0 5.0.0 4.8.0 4.7.1 4.7.0 4.6.0 4.5.0 4.4.10 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.1 4.3.0 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.1 4.1.0 4.0.0 3.16.0 3.15.3 3.15.2 3.15.1 3.14.0 3.13.0 3.12.1 3.12.0 3.11.1 3.11.0 3.10.3 3.10.2 3.10.1 3.9.0 3.8.0 3.7.0 3.5.0 3.4.0 3.3.0 3.2.0 3.1.0 3.0.0 2.1.1 2.1.0 2.0.0 1.5.2 1.5.1 1.4.30 1.4.29 1.4.28 1.4.27 1.4.26 1.4.25 1.4.24 1.4.23 1.4.22 1.4.21 1.4.20 1.4.19 1.4.18 1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.1 1.3.0 1.2.0 1.1.0 1.0.10 1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0 0.48.6 0.48.5 0.48.4 0.48.3 0.48.2 0.48.1 0.48.0 0.47.1 0.47.0 0.46.3 0.46.1 0.46.0 0.45.3 0.45.2 0.45.1 0.45.0 0.44.2 0.44.1 0.44.0 0.43.3 0.43.2 0.43.1 0.43.0 0.42.9 0.42.8 0.42.7 0.42.6 0.42.5 0.42.4 0.42.3 0.42.2 0.42.1 0.42.0 0.41.1 0.41.0 0.40.1 0.40.0 0.39.9 0.39.8 0.39.7 0.39.6 0.39.5 0.39.4 0.39.3 0.39.2 0.39.1 0.39.0 0.38.8 0.38.7 0.38.6 0.38.5 0.38.4 0.38.3 0.38.2 0.38.1 0.38.0 0.37.7 0.37.6 0.37.5 0.37.4 0.37.3 0.37.2 0.37.1 0.37.0 0.36.6 0.36.5 0.36.4 0.36.3 0.36.2 0.36.1 0.36.0 0.35.4 0.35.3 0.35.2 0.35.1 0.35.0 0.34.1 0.34.0 0.33.1 0.33.0 0.32.5 0.32.4 0.32.3 0.32.2 0.32.1 0.32.0 0.31.9 0.31.8 0.31.7 0.31.6 0.31.5 0.31.4 0.31.3 0.31.2 0.31.1 0.31.0 0.30.10 0.30.9 0.30.8 0.30.7 0.30.6 0.30.5 0.30.4 0.30.3 0.30.2 0.30.1 0.30.0 0.29.0 0.28.3 0.28.2 0.28.1 0.28.0 0.27.1 0.27.0 0.26.1 0.26.0 0.25.6 0.25.5 0.25.4 0.25.3 0.25.2 0.25.1 0.25.0 0.24.6 0.24.5 0.24.4 0.24.3 0.24.2 0.24.1 0.24.0 0.23.3 0.23.2 0.23.1 0.23.0 0.22.12 0.22.11 0.22.10 0.22.9 0.22.8 0.22.7 0.22.6 0.22.5 0.22.4 0.22.3 0.22.2 0.22.1 0.22.0 0.21.7 0.21.6 0.21.5 0.21.4 0.21.2 0.21.1 0.21.0 0.20.25 0.20.24 0.20.23 0.20.22 0.20.21 0.20.20 0.20.19 0.20.18 0.20.17 0.20.16 0.20.15 0.20.14 0.20.13 0.20.12 0.20.11 0.20.10 0.20.9 0.20.8 0.20.7 0.20.6 0.20.5 0.20.3 0.20.2 0.20.1 0.20.0 0.19.2 0.19.1 0.19.0 0.18.1 0.18.0 0.17.4 0.17.3 0.17.2 0.17.1 0.17.0 0.16.6 0.16.5 0.16.4 0.16.3 0.16.2 0.16.1 0.16.0 0.15.1 0.15.0 0.14.3 0.14.2 0.14.1 0.14.0 0.12.6 0.12.5 0.12.3 0.11.3 0.10.2 0.10.1 0.10.0 0.9.2 0.9.1 0.9.0 0.8.25 0.8.24 0.8.23 0.8.22 0.8.21 0.8.20 0.8.19 0.8.18 0.8.17 0.8.16 0.8.15 0.8.14 0.8.13 0.8.12 0.8.11 0.8.10 0.8.9 0.8.8 0.8.7 0.8.6 0.8.5 0.8.4 0.8.3 0.8.2 0.8.1 0.8.0 0.7.30 0.7.29 0.7.28 0.7.27 0.7.26 0.7.25 0.7.24 0.7.23 0.7.22 0.7.21 0.7.20 0.7.19 0.7.18 0.7.17 0.7.16 0.7.15 0.7.14 0.7.13 0.7.12 0.7.11 0.7.10 0.7.9 0.7.8 0.7.7 0.7.6 0.7.5 0.7.4 0.7.3 0.7.2 0.7.1 0.7.0 0.6.7 0.6.6 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.0 0.4.4 0.4.3 0.4.2 0.4.1 0.4.0 0.3.4 0.3.3 0.3.2 0.3.1

Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise

Current section

Files

Jump to
macula src macula_membership_member.erl
Raw

src/macula_membership_member.erl

%%%-------------------------------------------------------------------
%%% @doc
%%% Member record and state transitions for SWIM protocol.
%%% Represents a single node in the membership list.
%%% @end
%%%-------------------------------------------------------------------
-module(macula_membership_member).
%% API
-export([
new/2,
new/3,
node_id/1,
address/1,
status/1,
incarnation/1,
metadata/1,
mark_alive/2,
mark_suspect/1,
mark_dead/1,
merge/2,
compare/2
]).
%% Types
-type status() :: alive | suspect | dead.
-type member() :: #{
node_id := binary(),
address := {inet:ip_address(), inet:port_number()},
status := status(),
incarnation := non_neg_integer(),
metadata := map()
}.
-export_type([member/0, status/0]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Create a new member with alive status and incarnation 0.
-spec new(binary(), {inet:ip_address(), inet:port_number()}) -> member().
new(NodeId, Address) ->
new(NodeId, Address, #{}).
%% @doc Create a new member with custom metadata.
-spec new(binary(), {inet:ip_address(), inet:port_number()}, map()) -> member().
new(NodeId, Address, Metadata) ->
#{
node_id => NodeId,
address => Address,
status => alive,
incarnation => 0,
metadata => Metadata
}.
%% @doc Get node ID.
-spec node_id(member()) -> binary().
node_id(#{node_id := NodeId}) -> NodeId.
%% @doc Get address.
-spec address(member()) -> {inet:ip_address(), inet:port_number()}.
address(#{address := Address}) -> Address.
%% @doc Get status.
-spec status(member()) -> status().
status(#{status := Status}) -> Status.
%% @doc Get incarnation number.
-spec incarnation(member()) -> non_neg_integer().
incarnation(#{incarnation := Inc}) -> Inc.
%% @doc Get metadata.
-spec metadata(member()) -> map().
metadata(#{metadata := Meta}) -> Meta.
%% @doc Mark member as alive with new incarnation (refutation).
%% Dead members cannot be revived.
-spec mark_alive(member(), non_neg_integer()) -> member().
mark_alive(#{status := dead} = Member, _NewIncarnation) ->
%% Dead members stay dead
Member;
mark_alive(Member, NewIncarnation) ->
Member#{
status => alive,
incarnation => NewIncarnation
}.
%% @doc Mark member as suspect (failed to respond to ping).
-spec mark_suspect(member()) -> member().
mark_suspect(Member) ->
Member#{status => suspect}.
%% @doc Mark member as dead (confirmed failure).
-spec mark_dead(member()) -> member().
mark_dead(Member) ->
Member#{status => dead}.
%% @doc Merge two member states, keeping the most recent information.
%% Rules:
%% 1. Dead always wins
%% 2. Higher incarnation wins
%% 3. Same incarnation: suspect > alive
-spec merge(member(), member()) -> member().
merge(#{status := dead} = M1, _M2) ->
M1; % Dead wins
merge(_M1, #{status := dead} = M2) ->
M2; % Dead wins
merge(#{incarnation := Inc1} = M1, #{incarnation := Inc2}) when Inc1 > Inc2 ->
M1; % Higher incarnation wins
merge(#{incarnation := Inc1}, #{incarnation := Inc2} = M2) when Inc2 > Inc1 ->
M2; % Higher incarnation wins
merge(#{incarnation := Inc, status := Status1} = M1,
#{incarnation := Inc, status := Status2} = M2) ->
%% Same incarnation: suspect beats alive
case {Status1, Status2} of
{suspect, alive} -> M1;
{alive, suspect} -> M2;
_ -> M1 % Same status, keep first
end.
%% @doc Compare two members to determine which is more recent.
%% Returns: gt (M1 is newer), lt (M1 is older), eq (same)
-spec compare(member(), member()) -> gt | lt | eq.
compare(#{status := dead}, #{status := dead}) ->
eq;
compare(#{status := dead}, _) ->
gt; % Dead wins
compare(_, #{status := dead}) ->
lt; % Dead wins
compare(#{incarnation := Inc1}, #{incarnation := Inc2}) when Inc1 > Inc2 ->
gt;
compare(#{incarnation := Inc1}, #{incarnation := Inc2}) when Inc2 > Inc1 ->
lt;
compare(#{incarnation := Inc, status := Status1},
#{incarnation := Inc, status := Status2}) ->
%% Same incarnation: compare status
case {Status1, Status2} of
{suspect, alive} -> gt;
{alive, suspect} -> lt;
_ -> eq
end.