summaryrefslogtreecommitdiff
path: root/src/rabbit.erl
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 /src/rabbit.erl
parent9f6e7e7719883fa2e4c445ccdc73e4a162bfcea0 (diff)
downloadrabbitmq-server-git-ea4d5071524810f46e1c5dbeef8fbb51eeae232b.tar.gz
Try and reduce the distance to default
Diffstat (limited to 'src/rabbit.erl')
-rw-r--r--src/rabbit.erl199
1 files changed, 197 insertions, 2 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,