%%%------------------------------------------------------------------- %% @doc Macula Peers Supervisor. %% %% This supervisor manages dynamic peer connections using a %% simple_one_for_one strategy. Each peer connection gets its own %% macula_peer_system supervisor with dedicated handlers. %% %% Architecture (v0.8.5): %% - One macula_peers_sup instance per Macula node %% - Dynamically adds/removes macula_peer_system children %% - Each child = one peer connection %% - simple_one_for_one strategy for efficient scaling %% %% Usage: %%
%% %% Start new peer connection
%% {ok, PeerPid} = macula_peers_sup:start_peer(Url, Opts).
%%
%% %% List all active peers
%% Peers = macula_peers_sup:list_peers().
%%
%% %% Count active peers
%% Count = macula_peers_sup:count_peers().
%%
%% %% Stop peer connection
%% ok = macula_peers_sup:stop_peer(PeerPid).
%%
%%
%% @end
%%%-------------------------------------------------------------------
-module(macula_peers_sup).
-behaviour(supervisor).
%% API
-export([
start_link/0,
start_peer/2,
stop_peer/1,
list_peers/0,
count_peers/0
]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%%=============================================================================
%%% API Functions
%%%=============================================================================
%%------------------------------------------------------------------------------
%% @doc Start the peers supervisor.
%%
%% Called by macula_root during application startup.
%% Registers the supervisor with the local name macula_peers_sup.
%%
%% @end
%%------------------------------------------------------------------------------
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%------------------------------------------------------------------------------
%% @doc Start a new peer connection.
%%
%% Creates a new macula_peer_system supervisor for the peer at the given URL.
%% The peer system will manage the connection lifecycle and all peer-specific
%% handlers (pubsub, rpc, advertisements).
%%
%% @param Url Peer URL (e.g., "https://peer.example.com:4433")
%% @param Opts Connection options (realm, etc.)
%% @returns {ok, PeerPid} | {error, Reason}
%% @end
%%------------------------------------------------------------------------------
-spec start_peer(Url :: binary(), Opts :: map()) -> {ok, pid()} | {error, term()}.
start_peer(Url, Opts) when is_binary(Url), is_map(Opts) ->
supervisor:start_child(?SERVER, [Url, Opts]);
start_peer(Url, Opts) when is_list(Url), is_map(Opts) ->
start_peer(list_to_binary(Url), Opts).
%%------------------------------------------------------------------------------
%% @doc Stop a peer connection.
%%
%% Gracefully terminates the macula_peer_system supervisor for the given peer.
%% This will clean up all peer handlers and close the connection.
%%
%% @param PeerPid PID of the peer system supervisor to stop
%% @returns ok | {error, not_found}
%% @end
%%------------------------------------------------------------------------------
-spec stop_peer(PeerPid :: pid()) -> ok | {error, term()}.
stop_peer(PeerPid) when is_pid(PeerPid) ->
case supervisor:terminate_child(?SERVER, PeerPid) of
ok -> ok;
{error, not_found} -> {error, not_found};
{error, Reason} -> {error, Reason}
end.
%%------------------------------------------------------------------------------
%% @doc List all active peer connections.
%%
%% Returns a list of PIDs for all currently running macula_peer_system
%% supervisors.
%%
%% @returns [pid()]
%% @end
%%------------------------------------------------------------------------------
-spec list_peers() -> [pid()].
list_peers() ->
Children = supervisor:which_children(?SERVER),
[Pid || {_Id, Pid, _Type, _Modules} <- Children, is_pid(Pid)].
%%------------------------------------------------------------------------------
%% @doc Count active peer connections.
%%
%% Returns the number of currently running macula_peer_system supervisors.
%%
%% @returns non_neg_integer()
%% @end
%%------------------------------------------------------------------------------
-spec count_peers() -> non_neg_integer().
count_peers() ->
length(list_peers()).
%%%=============================================================================
%%% Supervisor Callbacks
%%%=============================================================================
%%------------------------------------------------------------------------------
%% @doc Initialize the peers supervisor.
%%
%% Sets up a simple_one_for_one supervisor for macula_peer_system children.
%% Each child will be started with dynamic arguments (Url, Opts).
%%
%% Strategy: simple_one_for_one
%% - Efficient for managing many similar children
%% - Children started/stopped dynamically
%% - All children use same child spec template
%%
%% Restart Strategy: temporary
%% - Peer connections don't auto-restart on crash
%% - Application decides when to reconnect
%% - Prevents reconnection storms
%%
%% @end
%%------------------------------------------------------------------------------
init([]) ->
io:format("Starting Peers Supervisor (simple_one_for_one)~n"),
SupFlags = #{
strategy => simple_one_for_one,
intensity => 10, % Max 10 restarts
period => 60 % In 60 seconds
},
%% Child spec template for macula_peer_system
%% Actual Url and Opts will be passed to start_peer/2
ChildSpec = #{
id => macula_peer_system, % Template ID (not used for simple_one_for_one)
start => {macula_peer_system, start_link, []}, % Args appended by start_child
restart => temporary, % Don't auto-restart failed peers
shutdown => 5000, % 5 second shutdown timeout
type => supervisor, % macula_peer_system is a supervisor
modules => [macula_peer_system]
},
{ok, {SupFlags, [ChildSpec]}}.