summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Watson <watson.timothy@gmail.com>2014-02-18 15:01:19 +0000
committerTim Watson <watson.timothy@gmail.com>2014-02-18 15:01:19 +0000
commitea4d5071524810f46e1c5dbeef8fbb51eeae232b (patch)
tree17f57307d09c5cfe75a9e5ecca6eca62ced14643
parent9f6e7e7719883fa2e4c445ccdc73e4a162bfcea0 (diff)
downloadrabbitmq-server-git-ea4d5071524810f46e1c5dbeef8fbb51eeae232b.tar.gz
Try and reduce the distance to default
-rw-r--r--src/rabbit.erl199
-rw-r--r--src/rabbit_boot.erl201
2 files changed, 207 insertions, 193 deletions
diff --git a/src/rabbit.erl b/src/rabbit.erl
index 3690329fe8..5d9aaa5ff6 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -318,7 +318,7 @@ start() ->
end).
boot() ->
- rabbit_boot:boot_with(
+ boot_with(
fun() ->
ok = ensure_application_loaded(),
Success = maybe_hipe_compile(),
@@ -336,6 +336,35 @@ boot() ->
ok = log_broker_started(Plugins)
end).
+handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) ->
+ throw({could_not_start, App, Reason});
+
+handle_app_error(App, Reason) ->
+ throw({could_not_start, App, Reason}).
+
+boot_with(StartFun) ->
+ Marker = spawn_link(fun() -> receive stop -> ok end end),
+ case catch register(rabbit_boot, Marker) of
+ true -> try
+ case rabbit:is_running() of
+ true -> ok;
+ false -> StartFun()
+ end
+ catch
+ throw:{could_not_start, _App, _Reason}=Err ->
+ boot_error(Err, not_available);
+ _:Reason ->
+ boot_error(Reason, erlang:get_stacktrace())
+ after
+ unlink(Marker),
+ Marker ! stop,
+ %% give the error loggers some time to catch up
+ timer:sleep(100)
+ end;
+ _ -> unlink(Marker),
+ Marker ! stop
+ end.
+
stop() ->
rabbit_log:info("Stopping RabbitMQ~n"),
rabbit_boot:shutdown(app_shutdown_order()).
@@ -420,7 +449,7 @@ start(normal, []) ->
print_banner(),
log_banner(),
rabbit_boot:prepare_boot_table(),
- rabbit_boot:run_boot_steps(),
+ run_boot_steps(),
{ok, SupPid};
Error ->
Error
@@ -447,6 +476,159 @@ app_shutdown_order() ->
app_utils:app_dependency_order(Apps, true).
%%---------------------------------------------------------------------------
+%% boot step logic
+
+run_boot_steps() ->
+ Steps = rabbit_boot:load_steps(boot),
+ [ok = run_boot_step(Step) || Step <- Steps],
+ ok.
+
+run_boot_step({_, StepName, Attributes}) ->
+ case catch rabbit_boot:already_run(StepName) of
+ false -> ok = run_step(StepName, Attributes, mfa),
+ rabbit_boot:mark_complete(StepName);
+ _ -> ok
+ end,
+ ok.
+
+run_step(StepName, Attributes, AttributeName) ->
+ case [MFA || {Key, MFA} <- Attributes,
+ Key =:= AttributeName] of
+ [] ->
+ ok;
+ MFAs ->
+ [try
+ apply(M,F,A)
+ of
+ ok -> ok;
+ {error, Reason} -> boot_error({boot_step, StepName, Reason},
+ not_available)
+ catch
+ _:Reason -> boot_error({boot_step, StepName, Reason},
+ erlang:get_stacktrace())
+ end || {M,F,A} <- MFAs],
+ ok
+ end.
+
+load_steps(Type) ->
+ StepAttrs = rabbit_misc:all_module_attributes_with_app(rabbit_boot_step),
+ sort_boot_steps(
+ Type,
+ lists:usort(
+ [{Mod, {AppName, Steps}} || {AppName, Mod, Steps} <- StepAttrs])).
+
+vertices(_Module, {AppName, Steps}) ->
+ [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps].
+
+edges(Type) ->
+ %% When running "boot" steps, both hard _and_ soft dependencies are
+ %% considered equally. When running "cleanup" steps however, we only
+ %% consider /hard/ dependencies (i.e., of the form
+ %% {DependencyType, {hard, StepName}}) as dependencies.
+ fun (_Module, {_AppName, Steps}) ->
+ [case Key of
+ requires -> {StepName, strip_type(OtherStep)};
+ enables -> {strip_type(OtherStep), StepName}
+ end || {StepName, Atts} <- Steps,
+ {Key, OtherStep} <- Atts,
+ filter_dependent_steps(Key, OtherStep, Type)]
+ end.
+
+filter_dependent_steps(Key, Dependency, Type)
+ when Key =:= requires orelse Key =:= enables ->
+ case {Dependency, Type} of
+ {{hard, _}, cleanup} -> true;
+ {_SoftReqs, cleanup} -> false;
+ {_, boot} -> true
+ end;
+filter_dependent_steps(_, _, _) ->
+ false.
+
+strip_type({hard, Step}) -> Step;
+strip_type(Step) -> Step.
+
+sort_boot_steps(Type, UnsortedSteps) ->
+ case rabbit_misc:build_acyclic_graph(fun vertices/2, edges(Type),
+ UnsortedSteps) of
+ {ok, G} ->
+ %% Use topological sort to find a consistent ordering (if
+ %% there is one, otherwise fail).
+ SortedSteps = lists:reverse(
+ [begin
+ {StepName, Step} = digraph:vertex(G,
+ StepName),
+ Step
+ end || StepName <- digraph_utils:topsort(G)]),
+ digraph:delete(G),
+ %% Check that all mentioned {M,F,A} triples are exported.
+ case [{StepName, {M,F,A}} ||
+ {_App, StepName, Attributes} <- SortedSteps,
+ {mfa, {M,F,A}} <- Attributes,
+ not erlang:function_exported(M, F, length(A))] of
+ [] -> SortedSteps;
+ MissingFunctions -> basic_boot_error(
+ {missing_functions, MissingFunctions},
+ "Boot step functions not exported: ~p~n",
+ [MissingFunctions])
+ end;
+ {error, {vertex, duplicate, StepName}} ->
+ basic_boot_error({duplicate_boot_step, StepName},
+ "Duplicate boot step name: ~w~n", [StepName]);
+ {error, {edge, Reason, From, To}} ->
+ basic_boot_error(
+ {invalid_boot_step_dependency, From, To},
+ "Could not add boot step dependency of ~w on ~w:~n~s",
+ [To, From,
+ case Reason of
+ {bad_vertex, V} ->
+ io_lib:format("Boot step not registered: ~w~n", [V]);
+ {bad_edge, [First | Rest]} ->
+ [io_lib:format("Cyclic dependency: ~w", [First]),
+ [io_lib:format(" depends on ~w", [Next]) ||
+ Next <- Rest],
+ io_lib:format(" depends on ~w~n", [First])]
+ end])
+ end.
+
+-ifdef(use_specs).
+-spec(boot_error/2 :: (term(), not_available | [tuple()]) -> no_return()).
+-endif.
+boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) ->
+ AllNodes = rabbit_mnesia:cluster_nodes(all),
+ {Err, Nodes} =
+ case AllNodes -- [node()] of
+ [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was"
+ " shut down forcefully~nit cannot determine which nodes"
+ " are timing out.~n", []};
+ Ns -> {rabbit_misc:format(
+ "Timeout contacting cluster nodes: ~p.~n", [Ns]),
+ Ns}
+ end,
+ basic_boot_error(Term,
+ Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []);
+boot_error(Reason, Stacktrace) ->
+ Fmt = "Error description:~n ~p~n~n" ++
+ "Log files (may contain more information):~n ~s~n ~s~n~n",
+ Args = [Reason, log_location(kernel), log_location(sasl)],
+ boot_error(Reason, Fmt, Args, Stacktrace).
+
+-ifdef(use_specs).
+-spec(boot_error/4 :: (term(), string(), [any()], not_available | [tuple()])
+ -> no_return()).
+-endif.
+boot_error(Reason, Fmt, Args, not_available) ->
+ basic_boot_error(Reason, Fmt, Args);
+boot_error(Reason, Fmt, Args, Stacktrace) ->
+ basic_boot_error(Reason, Fmt ++ "Stack trace:~n ~p~n~n",
+ Args ++ [Stacktrace]).
+
+basic_boot_error(Reason, Format, Args) ->
+ io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args),
+ rabbit_misc:local_info_msg(Format, Args),
+ timer:sleep(1000),
+ exit({?MODULE, failure_during_boot, Reason}).
+
+%%---------------------------------------------------------------------------
%% boot step functions
boot_delegate() ->
@@ -486,6 +668,19 @@ insert_default_data() ->
%%---------------------------------------------------------------------------
%% logging
+log_location(Type) ->
+ case application:get_env(rabbit, case Type of
+ kernel -> error_logger;
+ sasl -> sasl_error_logger
+ end) of
+ {ok, {file, File}} -> File;
+ {ok, false} -> undefined;
+ {ok, tty} -> tty;
+ {ok, silent} -> undefined;
+ {ok, Bad} -> throw({error, {cannot_log_to_file, Bad}});
+ _ -> undefined
+ end.
+
ensure_working_log_handlers() ->
Handlers = gen_event:which_handlers(error_logger),
ok = ensure_working_log_handler(error_logger_tty_h,
diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl
index d09864422a..4ceef0c70e 100644
--- a/src/rabbit_boot.erl
+++ b/src/rabbit_boot.erl
@@ -17,22 +17,20 @@
-module(rabbit_boot).
-export([prepare_boot_table/0]).
--export([boot_with/1, shutdown/1]).
+-export([shutdown/1]).
-export([start/1, stop/1]).
--export([run_boot_steps/0]).
--export([boot_error/2, boot_error/4]).
+-export([already_run/1, mark_complete/1]).
+-export([load_steps/1]).
-ifdef(use_specs).
-spec(prepare_boot_table/0 :: () -> 'ok').
--spec(boot_with/1 :: (fun(() -> 'ok')) -> 'ok').
+-spec(already_run/1 :: (atom()) -> boolean()).
+-spec(mark_complete/1 :: (atom()) -> 'ok').
-spec(shutdown/1 :: ([atom()]) -> 'ok').
-spec(start/1 :: ([atom()]) -> 'ok').
-spec(stop/1 :: ([atom()]) -> 'ok').
-spec(run_boot_steps/0 :: () -> 'ok').
--spec(boot_error/2 :: (term(), not_available | [tuple()]) -> no_return()).
--spec(boot_error/4 :: (term(), string(), [any()], not_available | [tuple()])
- -> no_return()).
-endif.
@@ -45,7 +43,7 @@
%% each application/plugin we're starting, plus any other (dependent) steps.
%% To achieve this, we process boot steps as usual, but skip those that have
%% already run (i.e., whilst, or even since the broker started).
-%%
+%%
%% Tracking which boot steps have run is done via a shared ets table, owned
%% by the "rabbit" process.
@@ -55,29 +53,6 @@
prepare_boot_table() ->
ets:new(?MODULE, [named_table, public, ordered_set]).
-boot_with(StartFun) ->
- Marker = spawn_link(fun() -> receive stop -> ok end end),
- case catch register(rabbit_boot, Marker) of
- true -> try
- case rabbit:is_running() of
- true -> ok;
- false -> StartFun()
- end
- catch
- throw:{could_not_start, _App, _Reason}=Err ->
- boot_error(Err, not_available);
- _:Reason ->
- boot_error(Reason, erlang:get_stacktrace())
- after
- unlink(Marker),
- Marker ! stop,
- %% give the error loggers some time to catch up
- timer:sleep(100)
- end;
- _ -> unlink(Marker),
- Marker ! stop
- end.
-
shutdown(Apps) ->
case whereis(?MODULE) of
undefined -> ok;
@@ -91,7 +66,7 @@ start(Apps) ->
force_reload(Apps),
StartupApps = app_utils:app_dependency_order(Apps, false),
case whereis(?MODULE) of
- undefined -> run_boot_steps();
+ undefined -> rabbit:run_boot_steps();
_ -> ok
end,
ok = app_utils:start_applications(StartupApps,
@@ -102,7 +77,7 @@ stop(Apps) ->
ok = app_utils:stop_applications(
Apps, handle_app_error(error_during_shutdown))
after
- BootSteps = load_steps(boot),
+ BootSteps = rabbit:load_steps(boot),
ToDelete = [Step || {App, _, _}=Step <- BootSteps,
lists:member(App, Apps)],
[ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete],
@@ -129,47 +104,10 @@ run_cleanup_steps(Apps) ->
end
end,
Completed,
- [Step || {App, _, _}=Step <- load_steps(cleanup),
+ [Step || {App, _, _}=Step <- rabbit:load_steps(cleanup),
lists:member(App, Apps)]),
ok.
-run_boot_steps() ->
- Steps = load_steps(boot),
- [ok = run_boot_step(Step) || Step <- Steps],
- ok.
-
-load_steps(Type) ->
- StepAttrs = rabbit_misc:all_module_attributes_with_app(rabbit_boot_step),
- sort_boot_steps(
- Type,
- lists:usort(
- [{Mod, {AppName, Steps}} || {AppName, Mod, Steps} <- StepAttrs])).
-
-boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) ->
- AllNodes = rabbit_mnesia:cluster_nodes(all),
- {Err, Nodes} =
- case AllNodes -- [node()] of
- [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was"
- " shut down forcefully~nit cannot determine which nodes"
- " are timing out.~n", []};
- Ns -> {rabbit_misc:format(
- "Timeout contacting cluster nodes: ~p.~n", [Ns]),
- Ns}
- end,
- basic_boot_error(Term,
- Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []);
-boot_error(Reason, Stacktrace) ->
- Fmt = "Error description:~n ~p~n~n" ++
- "Log files (may contain more information):~n ~s~n ~s~n~n",
- Args = [Reason, log_location(kernel), log_location(sasl)],
- boot_error(Reason, Fmt, Args, Stacktrace).
-
-boot_error(Reason, Fmt, Args, not_available) ->
- basic_boot_error(Reason, Fmt, Args);
-boot_error(Reason, Fmt, Args, Stacktrace) ->
- basic_boot_error(Reason, Fmt ++ "Stack trace:~n ~p~n~n",
- Args ++ [Stacktrace]).
-
%%---------------------------------------------------------------------------
%% Private API
@@ -211,34 +149,7 @@ handle_app_error(Term) ->
end.
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(StepName, Attributes, mfa),
- mark_complete(StepName);
- _ -> ok
- end,
- ok.
-
-run_step_name(StepName, Attributes, AttributeName) ->
- case [MFA || {Key, MFA} <- Attributes,
- Key =:= AttributeName] of
- [] ->
- ok;
- MFAs ->
- [try
- apply(M,F,A)
- of
- ok -> ok;
- {error, Reason} -> boot_error({boot_step, StepName, Reason},
- not_available)
- catch
- _:Reason -> boot_error({boot_step, StepName, Reason},
- erlang:get_stacktrace())
- end || {M,F,A} <- MFAs],
- ok
- end.
+ rabbit:run_step(StepName, Attributes, cleanup).
already_run(StepName) ->
ets:member(?MODULE, StepName).
@@ -246,95 +157,3 @@ already_run(StepName) ->
mark_complete(StepName) ->
ets:insert(?MODULE, {StepName}).
-basic_boot_error(Reason, Format, Args) ->
- io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args),
- rabbit_misc:local_info_msg(Format, Args),
- timer:sleep(1000),
- exit({?MODULE, failure_during_boot, Reason}).
-
-%% TODO: move me to rabbit_misc
-log_location(Type) ->
- case application:get_env(rabbit, case Type of
- kernel -> error_logger;
- sasl -> sasl_error_logger
- end) of
- {ok, {file, File}} -> File;
- {ok, false} -> undefined;
- {ok, tty} -> tty;
- {ok, silent} -> undefined;
- {ok, Bad} -> throw({error, {cannot_log_to_file, Bad}});
- _ -> undefined
- end.
-
-vertices(_Module, {AppName, Steps}) ->
- [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps].
-
-edges(Type) ->
- %% When running "boot" steps, both hard _and_ soft dependencies are
- %% considered equally. When running "cleanup" steps however, we only
- %% consider /hard/ dependencies (i.e., of the form
- %% {DependencyType, {hard, StepName}}) as dependencies.
- fun (_Module, {_AppName, Steps}) ->
- [case Key of
- requires -> {StepName, strip_type(OtherStep)};
- enables -> {strip_type(OtherStep), StepName}
- end || {StepName, Atts} <- Steps,
- {Key, OtherStep} <- Atts,
- filter_dependent_steps(Key, OtherStep, Type)]
- end.
-
-filter_dependent_steps(Key, Dependency, Type)
- when Key =:= requires orelse Key =:= enables ->
- case {Dependency, Type} of
- {{hard, _}, cleanup} -> true;
- {_SoftReqs, cleanup} -> false;
- {_, boot} -> true
- end;
-filter_dependent_steps(_, _, _) ->
- false.
-
-strip_type({hard, Step}) -> Step;
-strip_type(Step) -> Step.
-
-sort_boot_steps(Type, UnsortedSteps) ->
- case rabbit_misc:build_acyclic_graph(fun vertices/2, edges(Type),
- UnsortedSteps) of
- {ok, G} ->
- %% Use topological sort to find a consistent ordering (if
- %% there is one, otherwise fail).
- SortedSteps = lists:reverse(
- [begin
- {StepName, Step} = digraph:vertex(G,
- StepName),
- Step
- end || StepName <- digraph_utils:topsort(G)]),
- digraph:delete(G),
- %% Check that all mentioned {M,F,A} triples are exported.
- case [{StepName, {M,F,A}} ||
- {_App, StepName, Attributes} <- SortedSteps,
- {mfa, {M,F,A}} <- Attributes,
- not erlang:function_exported(M, F, length(A))] of
- [] -> SortedSteps;
- MissingFunctions -> basic_boot_error(
- {missing_functions, MissingFunctions},
- "Boot step functions not exported: ~p~n",
- [MissingFunctions])
- end;
- {error, {vertex, duplicate, StepName}} ->
- basic_boot_error({duplicate_boot_step, StepName},
- "Duplicate boot step name: ~w~n", [StepName]);
- {error, {edge, Reason, From, To}} ->
- basic_boot_error(
- {invalid_boot_step_dependency, From, To},
- "Could not add boot step dependency of ~w on ~w:~n~s",
- [To, From,
- case Reason of
- {bad_vertex, V} ->
- io_lib:format("Boot step not registered: ~w~n", [V]);
- {bad_edge, [First | Rest]} ->
- [io_lib:format("Cyclic dependency: ~w", [First]),
- [io_lib:format(" depends on ~w", [Next]) ||
- Next <- Rest],
- io_lib:format(" depends on ~w~n", [First])]
- end])
- end.