diff options
| author | Tim Watson <watson.timothy@gmail.com> | 2014-02-18 15:01:19 +0000 |
|---|---|---|
| committer | Tim Watson <watson.timothy@gmail.com> | 2014-02-18 15:01:19 +0000 |
| commit | ea4d5071524810f46e1c5dbeef8fbb51eeae232b (patch) | |
| tree | 17f57307d09c5cfe75a9e5ecca6eca62ced14643 /src/rabbit.erl | |
| parent | 9f6e7e7719883fa2e4c445ccdc73e4a162bfcea0 (diff) | |
| download | rabbitmq-server-git-ea4d5071524810f46e1c5dbeef8fbb51eeae232b.tar.gz | |
Try and reduce the distance to default
Diffstat (limited to 'src/rabbit.erl')
| -rw-r--r-- | src/rabbit.erl | 199 |
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, |
