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 | |
| parent | 9f6e7e7719883fa2e4c445ccdc73e4a162bfcea0 (diff) | |
| download | rabbitmq-server-git-ea4d5071524810f46e1c5dbeef8fbb51eeae232b.tar.gz | |
Try and reduce the distance to default
| -rw-r--r-- | src/rabbit.erl | 199 | ||||
| -rw-r--r-- | src/rabbit_boot.erl | 201 |
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. |
