summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rabbit.erl6
-rw-r--r--src/rabbit_boot.erl158
2 files changed, 83 insertions, 81 deletions
diff --git a/src/rabbit.erl b/src/rabbit.erl
index 081e2e229a..102d981572 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -41,6 +41,12 @@
{requires, pre_boot},
{enables, external_infrastructure}]}).
+-rabbit_boot_step({boot_table,
+ [{mfa, {rabbit_sup, start_child,
+ [rabbit_boot_table, rabbit_boot, []]}},
+ {requires, file_handle_cache},
+ {enables, external_infrastructure}]}).
+
-rabbit_boot_step({database,
[{mfa, {rabbit_mnesia, init, []}},
{requires, file_handle_cache},
diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl
index 1f8fcf1d19..1af1725dfc 100644
--- a/src/rabbit_boot.erl
+++ b/src/rabbit_boot.erl
@@ -17,10 +17,13 @@
-module(rabbit_boot).
-export([boot_with/1, shutdown/1]).
--export([start/1, stop/1]).
+-export([start_link/0, start/1, stop/1]).
-export([run_boot_steps/0]).
-export([boot_error/2, boot_error/4]).
+-export([init/1, handle_call/3, handle_cast/2,
+ handle_info/2, terminate/2, code_change/3]).
+
-ifdef(use_specs).
-spec(boot_with/1 :: (fun(() -> 'ok')) -> 'ok').
@@ -36,6 +39,9 @@
-define(BOOT_FILE, "boot.info").
+-define(INIT_SERVER, rabbit_boot_table_initial).
+-define(SERVER, rabbit_boot_table).
+
%%
%% When the broker is starting, we must run all the boot steps within the
%% rabbit:start/2 application callback, after rabbit_sup has started and
@@ -59,10 +65,47 @@
%%
%%---------------------------------------------------------------------------
+%% gen_server API - we use a gen_server to keep our ets table around for the
+%% lifetime of the broker. Since we can't add a gen_server to rabbit_sup
+%% during the initial boot phase (because boot-steps need evaluating prior to
+%% starting rabbit_sup), we overload the gen_server init/2 callback, providing
+%% an initial table owner and subsequent heir that gets hooked into rabbit_sup
+%% once it has started.
+
+start_link() -> gen_server:start_link(?MODULE, [?SERVER], []).
+
+init([?INIT_SERVER]) ->
+ ets:new(?MODULE, [named_table, public, ordered_set]),
+ {ok, ?INIT_SERVER};
+init(_) ->
+ %% if we crash (?), we'll need to re-create our ets table when rabbit_sup
+ %% restarts us, so there's no guarantee that the INIT_SERVER will be
+ %% alive when we get here!
+ case catch gen_server:call(?INIT_SERVER, {inheritor, self()}) of
+ {'EXIT', {noproc, _}} -> init([?INIT_SERVER]);
+ ok -> ok
+ end,
+ {ok, ?SERVER}.
+
+handle_call({inheritor, Pid}, From, ?INIT_SERVER) ->
+ %% setting the hier seems simpler than using give_away here
+ ets:setopts(?MODULE, [{heir, Pid, []}]),
+ gen_server:reply(From, ok),
+ {stop, normal, ?INIT_SERVER};
+handle_call(_, _From, ?SERVER) ->
+ {noreply, ?SERVER}.
+
+handle_cast(_, State) -> {noreply, State}.
+handle_info(_, State) -> {noreply, State}.
+terminate(_, _) -> ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%---------------------------------------------------------------------------
%% Public API
boot_with(StartFun) ->
- %% TODO: this should be done with monitors, not links, I think
Marker = spawn_link(fun() -> receive stop -> ok end end),
case catch register(rabbit_boot, Marker) of
true -> try
@@ -86,32 +129,24 @@ boot_with(StartFun) ->
end.
shutdown(Apps) ->
- try
- case whereis(?MODULE) of
- undefined -> ok;
- _ -> await_startup(Apps)
- end,
- %% TODO: put this back in somewhere sensible...
- %% rabbit_log:info("Stopping RabbitMQ~n"),
- ok = app_utils:stop_applications(Apps)
- after
- delete_boot_table()
- end.
+ case whereis(?MODULE) of
+ undefined -> ok;
+ _ -> await_startup(Apps)
+ end,
+ %% TODO: put this back in somewhere sensible...
+ %% rabbit_log:info("Stopping RabbitMQ~n"),
+ ok = app_utils:stop_applications(Apps).
start(Apps) ->
- try
- ensure_boot_table(),
- force_reload(Apps),
- StartupApps = app_utils:app_dependency_order(Apps, false),
- case whereis(?MODULE) of
- undefined -> run_boot_steps();
- _ -> ok
- end,
- ok = app_utils:start_applications(StartupApps,
- handle_app_error(could_not_start))
- after
- save_boot_table()
- end.
+ ensure_boot_table(),
+ force_reload(Apps),
+ StartupApps = app_utils:app_dependency_order(Apps, false),
+ case whereis(?MODULE) of
+ undefined -> run_boot_steps();
+ _ -> ok
+ end,
+ ok = app_utils:start_applications(StartupApps,
+ handle_app_error(could_not_start)).
stop(Apps) ->
ensure_boot_table(),
@@ -119,15 +154,11 @@ stop(Apps) ->
ok = app_utils:stop_applications(
Apps, handle_app_error(error_during_shutdown))
after
- try
- BootSteps = load_steps(boot),
- ToDelete = [Step || {App, _, _}=Step <- BootSteps,
- lists:member(App, Apps)],
- [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete],
- run_cleanup_steps(Apps)
- after
- save_boot_table()
- end,
+ BootSteps = load_steps(boot),
+ ToDelete = [Step || {App, _, _}=Step <- BootSteps,
+ lists:member(App, Apps)],
+ [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete],
+ run_cleanup_steps(Apps),
[begin
{ok, Mods} = application:get_key(App, modules),
[begin
@@ -224,50 +255,13 @@ load_mod(Mod) ->
await_startup(Apps) ->
app_utils:wait_for_applications(Apps).
-delete_boot_table() ->
- case filelib:is_file(boot_file()) of
- true -> file:delete(boot_file());
- false -> ok
- end.
-
ensure_boot_table() ->
- case whereis(?MODULE) of
- undefined ->
- case filelib:is_file(boot_file()) of
- true -> load_table();
- false -> clean_table()
- end;
- _Pid ->
- clean_table()
- end.
-
-clean_table() ->
- delete_boot_table(),
case ets:info(?MODULE) of
- undefined ->
- ets:new(?MODULE, [named_table, public, ordered_set]);
- _ ->
- ok
+ undefined -> gen_server:start({local, ?INIT_SERVER}, ?MODULE,
+ [?INIT_SERVER], []);
+ _Pid -> ok
end.
-load_table() ->
- case ets:file2tab(boot_file(), [{verify, true}]) of
- {error, _} -> clean_table();
- {ok, _Tab} -> ok
- end.
-
-save_boot_table() ->
- delete_boot_table(),
- case ets:info(?MODULE) of
- undefined -> ok;
- _ -> ets:tab2file(?MODULE, boot_file(),
- [{extended_info, [object_count]}]),
- ets:delete(?MODULE)
- end.
-
-boot_file() ->
- filename:join(rabbit_mnesia:dir(), ?BOOT_FILE).
-
handle_app_error(Term) ->
fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) ->
throw({Term, App, ExitReason});
@@ -275,18 +269,18 @@ handle_app_error(Term) ->
throw({Term, App, Reason})
end.
-run_cleanup_step({_, _, Attributes}) ->
- run_step_name(Attributes, cleanup).
+run_cleanup_step({_, StepName, Attributes}) ->
+ run_step_name(StepName, Attributes, cleanup).
run_boot_step({_, StepName, Attributes}) ->
case catch already_run(StepName) of
- false -> ok = run_step_name(Attributes, mfa),
+ false -> ok = run_step_name(StepName, Attributes, mfa),
mark_complete(StepName);
_ -> ok
end,
ok.
-run_step_name(Attributes, AttributeName) ->
+run_step_name(StepName, Attributes, AttributeName) ->
case [MFA || {Key, MFA} <- Attributes,
Key =:= AttributeName] of
[] ->
@@ -296,9 +290,11 @@ run_step_name(Attributes, AttributeName) ->
apply(M,F,A)
of
ok -> ok;
- {error, Reason} -> boot_error(Reason, not_available)
+ {error, Reason} -> boot_error({boot_step, StepName, Reason},
+ not_available)
catch
- _:Reason -> boot_error(Reason, erlang:get_stacktrace())
+ _:Reason -> boot_error({boot_step, StepName, Reason},
+ erlang:get_stacktrace())
end || {M,F,A} <- MFAs],
ok
end.