Packages

macula

0.28.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_peer_system macula_peer_system.erl
Raw

src/macula_peer_system/macula_peer_system.erl

%%%-------------------------------------------------------------------
%%% @doc
%%% Peer System Supervisor - supervises the peer subsystem.
%%%
%%% Supervision Strategy:
%%% - rest_for_one: If child N crashes, restart N and all children after N
%%% - Rationale: connection_manager is foundational; handlers depend on it
%%% but are independent of each other. This provides fault isolation
%%% while maintaining consistency when connection_manager restarts.
%%%
%%% Architecture:
%%% <pre>
%%% macula_peer_system (this module)
%%% ├── macula_connection - QUIC connection lifecycle (transport layer)
%%% ├── macula_pubsub_handler - Pub/sub operations
%%% ├── macula_rpc_handler - RPC operations
%%% └── macula_advertisement_manager - DHT service advertisements
%%% </pre>
%%%
%%% Children (in dependency order):
%%% - macula_connection: QUIC connection lifecycle (foundational)
%%% - macula_pubsub_handler: Pub/sub operations (depends on connection)
%%% - macula_rpc_handler: RPC operations (depends on connection)
%%% - macula_advertisement_manager: DHT advertisements (depends on connection)
%%%
%%% Fault Isolation:
%%% - advertisement_manager crash → only advertisement restarts
%%% - rpc_handler crash → rpc + advertisement restart
%%% - pubsub_handler crash → pubsub + rpc + advertisement restart
%%% - connection crash → all restart (unavoidable - foundational)
%%%
%%% Renamed from macula_connection_sup (v0.7.10) to align with macula_peer
%%% nomenclature and macula_gateway_system naming convention.
%%% @end
%%%-------------------------------------------------------------------
-module(macula_peer_system).
-behaviour(supervisor).
-include_lib("kernel/include/logger.hrl").
%% API
-export([start_link/2, stop/1]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API
%%%===================================================================
%% @doc Start the peer system supervisor with given URL and options.
-spec start_link(binary(), map()) -> {ok, pid()} | {error, term()}.
start_link(Url, Opts) ->
case supervisor:start_link(?MODULE, {Url, Opts}) of
{ok, SupPid} ->
%% Initialize handlers after supervisor has started
%% This must be done asynchronously to avoid blocking the start_link call
spawn(fun() -> init_handlers(SupPid) end),
{ok, SupPid};
Error ->
Error
end.
%% @doc Stop the peer system supervisor and all children.
-spec stop(pid()) -> ok.
stop(Sup) when is_pid(Sup) ->
exit(Sup, shutdown),
ok.
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
%% @doc Initialize the supervisor with child specifications.
-spec init({binary(), map()}) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init({Url, Opts}) ->
?LOG_INFO("Starting peer system supervisor for ~s", [Url]),
%% Generate a unique peer_id for this peer system
%% Used for gproc registration to support multiple peer connections per realm
PeerId = erlang:unique_integer([monotonic, positive]),
%% Generate node_id ONCE and share across all children.
%% Without this, each child (connection, pubsub_handler, rpc_handler,
%% advertisement_manager) generates its own random node_id, causing
%% the CONNECT identity to diverge from the DHT subscription identity.
NodeId = maps:get(node_id, Opts, macula_utils:generate_node_id()),
OptsWithPeerId = Opts#{peer_id => PeerId, node_id => NodeId},
%% Supervision strategy: rest_for_one
%% If child N crashes, restart N and all children after N
%% - connection_manager crash → all restart (they depend on it)
%% - handler crash → only that handler and later ones restart (fault isolation)
SupFlags = #{
strategy => rest_for_one,
intensity => 10, % Max 10 restarts (increased tolerance)
period => 60 % Within 60 seconds (longer period)
},
%% Children in dependency order (connection_manager is foundational)
ChildSpecs = [
%% 1. Connection Manager (must start first)
#{
id => connection_manager,
start => {macula_connection, start_link, [Url, OptsWithPeerId]},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [macula_connection]
},
%% 2. PubSub Handler
#{
id => pubsub_handler,
start => {macula_pubsub_handler, start_link, [OptsWithPeerId]},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [macula_pubsub_handler]
},
%% 3. RPC Handler
#{
id => rpc_handler,
start => {macula_rpc_handler, start_link, [OptsWithPeerId]},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [macula_rpc_handler]
},
%% 4. Advertisement Manager
#{
id => advertisement_manager,
start => {macula_advertisement_manager, start_link, [OptsWithPeerId]},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [macula_advertisement_manager]
}
],
%% Schedule initialization of handlers after supervisor starts
%% This must be done asynchronously because supervisor init/1 must return
%% before children can be queried
%% Note: We return the supervisor PID in the return value, but we need to
%% handle initialization in the start_link callback instead
{ok, {SupFlags, ChildSpecs}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
%% @private
%% @doc Initialize handlers by sending them the connection_manager_pid.
%% This must be called after the supervisor has started and children are running.
%% The function waits a brief moment for children to fully initialize, then
%% looks up their PIDs and sends the set_connection_manager_pid message.
-spec init_handlers(pid()) -> ok.
init_handlers(SupPid) ->
%% Brief delay to ensure supervisor and children are fully initialized
timer:sleep(50),
%% Look up child PIDs from supervisor
case supervisor:which_children(SupPid) of
Children when is_list(Children) ->
%% Extract PIDs for each child
ConnMgrPid = find_child_pid(Children, connection_manager),
PubSubPid = find_child_pid(Children, pubsub_handler),
RpcPid = find_child_pid(Children, rpc_handler),
AdvMgrPid = find_child_pid(Children, advertisement_manager),
%% Send connection_manager_pid to children that need it
case ConnMgrPid of
undefined ->
?LOG_ERROR("Connection manager not found in peer system");
_ ->
send_if_pid(PubSubPid, {set_connection_manager_pid, ConnMgrPid}),
send_if_pid(RpcPid, {set_connection_manager_pid, ConnMgrPid}),
send_if_pid(AdvMgrPid, {set_connection_manager_pid, ConnMgrPid}),
?LOG_INFO("Initialized handlers with connection_manager_pid: ~p", [ConnMgrPid])
end;
_ ->
?LOG_ERROR("Failed to query supervisor children")
end,
ok.
%% @private
%% @doc Find a child PID by child ID in supervisor children list.
-spec find_child_pid(list(), atom()) -> pid() | undefined.
find_child_pid(Children, ChildId) ->
case lists:keyfind(ChildId, 1, Children) of
{ChildId, Pid, _Type, _Modules} when is_pid(Pid) -> Pid;
_ -> undefined
end.
%% @private
%% @doc Send a cast message to a PID if it's valid.
-spec send_if_pid(pid() | undefined, term()) -> ok.
send_if_pid(undefined, _Msg) -> ok;
send_if_pid(Pid, Msg) when is_pid(Pid) ->
gen_server:cast(Pid, Msg),
ok.