Packages
ra
3.1.1
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-beta.1
2.17.3
2.17.2
2.17.1
2.17.0
2.16.13
2.16.12
2.16.11
2.16.10
2.16.9
2.16.8
2.16.7
2.16.6
2.16.5
2.16.4
2.16.3
2.16.2
2.16.1
2.16.0
2.16.0-pre.12
2.16.0-pre.11
2.16.0-pre.10
2.16.0-pre.9
2.16.0-pre.8
2.16.0-pre.7
2.16.0-pre.6
2.16.0-pre.5
2.16.0-pre.4
2.16.0-pre.3
2.16.0-pre.2
2.16.0-pre.1
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.13.0-pre.1
2.12.0
2.11.0
2.11.0-pre.1
2.10.2-pre.2
2.10.2-pre.1
2.10.1
2.10.0
2.10.0-pre.3
2.10.0-pre.2
2.10.0-pre.1
2.9.10-pre.1
2.9.1
2.9.1-pre.2
2.9.1-pre.1
2.9.0
2.8.0
retired
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-pre.3
2.7.0-pre.2
2.7.0-pre.1
2.6.3
2.6.2
2.6.1
2.6.0-pre.1
2.5.1
2.5.1-pre.1
2.5.0
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
retired
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.13
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
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.9.6
0.9.5
0.9.4
0.9.2
0.3.3
retired
0.3.2
retired
0.3.1
retired
Raft library
Current section
Files
Jump to
Current section
Files
src/ra_server_sup_sup.erl
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2017-2025 Broadcom. All Rights Reserved. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries.
%%
%% @hidden
-module(ra_server_sup_sup).
-behaviour(supervisor).
-define(MUTABLE_CONFIG_KEYS,
[cluster_name,
metrics_key,
broadcast_time,
tick_timeout,
install_snap_rpc_timeout,
await_condition_timeout,
max_pipeline_count,
ra_event_formatter,
min_recovery_checkpoint_interval]).
%% API functions
-export([start_server/2,
restart_server/3,
stop_server/2,
delete_server/2,
remove_all/1,
start_link/1,
recover_config/2,
% for rpcs only
start_server_rpc/3,
restart_server_rpc/3,
delete_server_rpc/2,
prepare_server_stop_rpc/2]).
%% Supervisor callbacks
-export([init/1]).
-include("ra.hrl").
-spec start_server(System :: atom(), ra_server:ra_server_config()) ->
supervisor:startchild_ret() |
{error, not_new | system_not_started | invalid_initial_machine_version} |
{badrpc, term()}.
start_server(System, #{id := NodeId,
uid := UId} = Config)
when is_atom(System) ->
Node = ra_lib:ra_server_id_node(NodeId),
rpc:call(Node, ?MODULE, start_server_rpc, [System, UId, Config]).
-spec restart_server(atom(), ra_server_id(), ra_server:mutable_config()) ->
supervisor:startchild_ret() | {error, system_not_started} | {badrpc, term()}.
restart_server(System, {RaName, Node}, AddConfig) ->
rpc:call(Node, ?MODULE, restart_server_rpc,
[System, {RaName, Node}, AddConfig]).
start_server_rpc(System, UId, Config0) ->
case ra_system:fetch(System) of
undefined ->
{error, system_not_started};
SysCfg ->
Config = Config0#{system_config => SysCfg},
%% check that the server isn't already registered
case ra_directory:name_of(System, UId) of
undefined ->
case validate_config(Config) of
ok ->
case ra_system:lookup_name(System, server_sup) of
{ok, Name} ->
start_child(Name, Config);
Err ->
Err
end;
Err ->
Err
end;
Name ->
case whereis(Name) of
undefined ->
{error, not_new};
Pid ->
{error, {already_started, Pid}}
end
end
end.
validate_config(#{system_config := SysConf} = Config) ->
Strat = maps:get(machine_upgrade_strategy, SysConf, all),
case Config of
#{initial_machine_version := InitMacVer,
machine := {module, Mod, Args}} when Strat == all ->
MacVer = ra_machine:version({machine, Mod, Args}),
if MacVer < InitMacVer ->
{error, invalid_initial_machine_version};
true ->
ok
end;
_ ->
ok
end.
restart_server_rpc(System, {RaName, _Node}, AddConfig)
when is_atom(System) ->
case ra_system:fetch(System) of
undefined ->
{error, system_not_started};
SysCfg ->
case recover_config(System, RaName) of
{ok, Config0} ->
MutConfig = maps:with(?MUTABLE_CONFIG_KEYS, AddConfig),
{ok, Name} = ra_system:lookup_name(System, server_sup),
Config = case maps:merge(Config0, MutConfig) of
Config0 ->
%% the config has not changed
Config0#{system_config => SysCfg,
has_changed => false};
Config1 ->
Config1#{system_config => SysCfg,
has_changed => true}
end,
start_child(Name, Config);
Err ->
Err
end
end.
-spec stop_server(System :: atom(), RaNodeId :: ra_server_id()) ->
ok | {error, term()}.
stop_server(System, ServerId) when is_atom(System) ->
Node = ra_lib:ra_server_id_node(ServerId),
RaName = ra_lib:ra_server_id_to_local_name(ServerId),
Res = rpc:call(Node, ?MODULE,
prepare_server_stop_rpc, [System, RaName]),
case Res of
{error, _} = Err ->
Err;
{ok, Pid, SrvSup} when is_pid(Pid) ->
supervisor:terminate_child({SrvSup, Node}, Pid);
{ok, undefined, _} ->
%% no parent - no need to stop
ok;
Err ->
{error, Err}
end.
prepare_server_stop_rpc(System, RaName) ->
case ra_system:fetch(System) of
undefined ->
{error, system_not_started};
#{names := #{server_sup := SrvSup} = Names} ->
Parent = ra_directory:where_is_parent(Names, RaName),
{ok, Parent, SrvSup}
end.
-spec delete_server(atom(), ServerId :: ra_server_id()) ->
ok | {error, term()} | {badrpc, term()}.
delete_server(System, ServerId) when is_atom(System) ->
Node = ra_lib:ra_server_id_node(ServerId),
Name = ra_lib:ra_server_id_to_local_name(ServerId),
case stop_server(System, ServerId) of
ok ->
rpc:call(Node, ?MODULE, delete_server_rpc, [System, Name]);
{error, _} = Err -> Err
end.
delete_server_rpc(System, RaName) ->
case ra_system:fetch(System) of
undefined ->
{error, system_not_started};
#{data_dir := _SysDir,
names := #{log_meta := Meta,
server_sup := SrvSup} = Names} ->
?INFO("Deleting server ~w and its data directory.",
[RaName]),
%% TODO: better handle and report errors
%% UId could be `undefined' here
UId = ra_directory:uid_of(Names, RaName),
Pid = ra_directory:where_is(Names, RaName),
ra_log_meta:delete(Meta, UId),
Dir = ra_env:server_data_dir(System, UId),
_ = supervisor:terminate_child(SrvSup, UId),
_ = delete_data_directory(Dir),
_ = ra_directory:unregister_name(Names, UId),
%% forcefully clean up ETS tables
catch ets:delete(ra_log_metrics, UId),
catch ets:delete(ra_log_snapshot_state, UId),
catch ets:delete(ra_state, RaName),
catch ets:delete(ra_open_file_metrics, Pid),
catch ra_counters:delete({RaName, node()}),
catch ra_leaderboard:clear(RaName),
ok
end.
delete_data_directory(Directory) ->
DeleteFunction = fun() ->
try ra_lib:recursive_delete(Directory) of
ok ->
% moving on
ok
catch
_:_ = Err ->
?WARN("ra: delete_server/1 failed to delete directory ~ts. "
"Error: ~p", [Directory, Err]),
error
end
end,
case DeleteFunction() of
ok ->
ok;
_ ->
spawn(fun() ->
ra_lib:retry(DeleteFunction, 2)
end)
end.
remove_all(System) when is_atom(System) ->
#{names := #{server_sup := Sup}} = ra_system:fetch(System),
_ = [begin
?DEBUG("ra: terminating child ~w in system ~ts", [Pid, System]),
supervisor:terminate_child(Sup, Pid)
end
|| {_, Pid, _, _} <- supervisor:which_children(Sup)],
ok.
recover_config(System, RaName) ->
case ra_directory:uid_of(System, RaName) of
undefined ->
{error, name_not_registered};
UId ->
Dir = ra_env:server_data_dir(System, UId),
case ra_directory:where_is(System, UId) of
Pid when is_pid(Pid) ->
case is_process_alive(Pid) of
true ->
{error, {already_started, Pid}};
false ->
ra_log:read_config(Dir)
end;
_ ->
% can it be made generic without already knowing the config state?
ra_log:read_config(Dir)
end
end.
-spec start_link(ra_system:config()) ->
{ok, pid()} | ignore | {error, term()}.
start_link(#{names := #{server_sup := Name}}) ->
supervisor:start_link({local, Name}, ?MODULE, []).
init([]) ->
SupFlags = #{strategy => simple_one_for_one},
ChildSpec = #{id => undefined,
type => supervisor,
restart => temporary,
start => {ra_server_sup, start_link, []}},
{ok, {SupFlags, [ChildSpec]}}.
start_child(Name, Config) ->
Ref = make_ref(),
case supervisor:start_child(Name, [Config#{reply_to => {Ref, self()}}]) of
{ok, Pid} ->
%% we have started the process now and have to wait for reply
%% that is sent after init but before state machine recovery
MRef = erlang:monitor(process, Pid),
receive
{Ref, ok} ->
_ = erlang:demonitor(MRef),
{ok, Pid};
{'DOWN', MRef, _, _, Reason} ->
?ERROR("Ra: failed to start ra server ~ts, err ~s",
[Name, Reason]),
{error, Reason}
end;
Err ->
Err
end.