diff options
| author | Tim Watson <watson.timothy@gmail.com> | 2014-03-13 22:49:16 +0000 |
|---|---|---|
| committer | Tim Watson <watson.timothy@gmail.com> | 2014-03-13 22:49:16 +0000 |
| commit | 4a743ceba66a1190f5bcc445615e8e3f0641f080 (patch) | |
| tree | 6ff91868c3aaaf67661bef96e5ccc20377c490a4 | |
| parent | 5308e763c10b31e8becef7ba7b26bd507fa404b2 (diff) | |
| download | rabbitmq-server-git-4a743ceba66a1190f5bcc445615e8e3f0641f080.tar.gz | |
Refactor: Simplify boot/cleanup step handling and unify modules
Isolate boot steps to a set of applications and avoid using ets to
track which have run. This also simplifies cleanup step execution.
The force_reload code isn't needed, since the real issue preventing
updated versions of modules from being found lay in the proper
expansion of (the correct set of) .ez archives, which was fixed in
revision 1918e77.
Unify all the boot handling code under rabbit.erl once again and now
completely remove rabbit_boot, since it's no longer needed. Remove
unused exports and tidy.
| -rw-r--r-- | src/rabbit.erl | 137 | ||||
| -rw-r--r-- | src/rabbit_boot.erl | 127 | ||||
| -rw-r--r-- | src/rabbit_plugins.erl | 2 |
3 files changed, 91 insertions, 175 deletions
diff --git a/src/rabbit.erl b/src/rabbit.erl index 46838364d2..c703fedb35 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -24,7 +24,7 @@ start_fhc/0]). -export([run_boot_steps/0, load_steps/1, run_step/3]). -export([start/2, stop/1]). --export([handle_app_error/1, start_apps/1]). +-export([start_apps/1, stop_apps/1]). -export([log_location/1]). %% for testing %%--------------------------------------------------------------------------- @@ -242,7 +242,6 @@ -spec(maybe_insert_default_data/0 :: () -> 'ok'). -spec(boot_delegate/0 :: () -> 'ok'). -spec(recover/0 :: () -> 'ok'). --spec(handle_app_error/1 :: (term()) -> fun((atom(), term()) -> no_return())). -endif. @@ -343,10 +342,10 @@ handle_app_error(Term) -> end. start_apps(Apps) -> - rabbit_boot:force_reload(Apps), + app_utils:load_applications(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), case whereis(rabbit_boot) of - undefined -> rabbit:run_boot_steps(); + undefined -> run_boot_steps(Apps); _ -> ok end, ok = app_utils:start_applications(StartupApps, @@ -393,6 +392,29 @@ stop_and_halt() -> end, ok. +stop_apps(Apps) -> + try + ok = app_utils:stop_applications( + Apps, handle_app_error(error_during_shutdown)) + after + run_cleanup_steps(Apps), + [begin + {ok, Mods} = application:get_key(App, modules), + [begin + code:soft_purge(Mod), + code:delete(Mod), + false = code:is_loaded(Mod) + end || Mod <- Mods], + application:unload(App) + end || App <- Apps] + end. + +run_cleanup_steps(Apps) -> + [run_step(Name, Attributes, cleanup) || + {App, Name, Attributes} <- load_steps(Apps), + lists:member(App, Apps)], + ok. + await_startup() -> app_utils:wait_for_applications(app_startup_order()). @@ -463,7 +485,6 @@ start(normal, []) -> true = register(rabbit, self()), print_banner(), log_banner(), - rabbit_boot:prepare_boot_table(), run_boot_steps(), {ok, SupPid}; Error -> @@ -493,16 +514,12 @@ app_shutdown_order() -> %% boot step logic run_boot_steps() -> - Steps = load_steps(boot), - [ok = run_boot_step(Step) || Step <- Steps], - ok. + run_boot_steps([App || {App, _, _} <- application:loaded_applications()]). -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, +run_boot_steps(Apps) -> + Steps = load_steps(Apps), + [ok = run_step(StepName, Attributes, mfa) || + {_, StepName, Attributes} <- Steps], ok. run_step(StepName, Attributes, AttributeName) -> @@ -524,45 +541,71 @@ run_step(StepName, Attributes, AttributeName) -> ok end. -load_steps(Type) -> +load_steps(BaseApps) -> + Apps = BaseApps -- app_utils:which_applications(), %% exclude running apps StepAttrs = rabbit_misc:all_module_attributes_with_app(rabbit_boot_step), - sort_boot_steps( - Type, - lists:usort( - [{Mod, {AppName, Steps}} || {AppName, Mod, Steps} <- StepAttrs])). + {AllSteps, StepsDict} = + lists:foldl( + fun({AppName, Mod, Steps}, {AccSteps, AccDict}) -> + {[{Mod, {AppName, Steps}}|AccSteps], + lists:foldl( + fun({StepName, _}, Acc) -> + dict:store(StepName, AppName, Acc) + end, AccDict, Steps)} + end, {[], dict:new()}, StepAttrs), + Steps = lists:foldl(filter_steps(Apps, StepsDict), [], AllSteps), + sort_boot_steps(lists:usort(Steps)). + +filter_steps(Apps, Dict) -> + fun({Mod, {AppName, Steps}}, Acc) -> + Steps2 = [begin + Filtered = lists:foldl(filter_attrs(Apps, Dict), + [], Attrs), + {Step, Filtered} + end || {Step, Attrs} <- Steps, + filter_app(Apps, Dict, Step)], + [{Mod, {AppName, Steps2}}|Acc] + end. + +filter_app(Apps, Dict, Step) -> + case dict:find(Step, Dict) of + {ok, App} -> lists:member(App, Apps); + error -> false + end. + +filter_attrs(Apps, Dict) -> + fun(Attr={Type, Other}, AccAttrs) when Type =:= requires orelse + Type =:= enables -> + %% If we don't know about a dependency, we allow it through, + %% since we don't *know* that it should be ignored. If, on + %% the other hand, we recognise a dependency then we _only_ + %% include it (i.e., the requires/enables attribute itself) + %% if the referenced step comes from one of the Apps we're + %% actively working with at this point. + case dict:find(Other, Dict) of + error -> [Attr | AccAttrs]; + {ok, App} -> case lists:member(App, Apps) of + true -> [Attr | AccAttrs]; + false -> AccAttrs + end + end; + (Attr, AccAttrs) -> + [Attr | AccAttrs] + 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. +edges(_Module, {_AppName, Steps}) -> + [case Key of + requires -> {StepName, OtherStep}; + enables -> {OtherStep, StepName} + end || {StepName, Atts} <- Steps, + {Key, OtherStep} <- Atts, + Key =:= requires orelse Key =:= enables]. -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), +sort_boot_steps(UnsortedSteps) -> + case rabbit_misc:build_acyclic_graph(fun vertices/2, fun edges/2, UnsortedSteps) of {ok, G} -> %% Use topological sort to find a consistent ordering (if diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl deleted file mode 100644 index e9bf5457de..0000000000 --- a/src/rabbit_boot.erl +++ /dev/null @@ -1,127 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License -%% at http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and -%% limitations under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. -%% - --module(rabbit_boot). - --export([prepare_boot_table/0]). --export([stop/1]). --export([force_reload/1]). --export([already_run/1, mark_complete/1]). - --ifdef(use_specs). - --spec(prepare_boot_table/0 :: () -> 'ok'). --spec(already_run/1 :: (atom()) -> boolean()). --spec(mark_complete/1 :: (atom()) -> 'ok'). --spec(stop/1 :: ([atom()]) -> 'ok'). --spec(force_reload/1 :: ([atom()]) -> 'ok'). - --endif. - -%% 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 -%% before any plugin applications begin to start. To achieve this, we process -%% the boot steps from all loaded applications. -%% -%% If the broker is already running however, we must run all boot steps for -%% 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. - -%%--------------------------------------------------------------------------- -%% Public API - -prepare_boot_table() -> - ets:new(?MODULE, [named_table, public, ordered_set]). - -stop(Apps) -> - try - ok = app_utils:stop_applications( - Apps, rabbit:handle_app_error(error_during_shutdown)) - after - BootSteps = rabbit: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 - code:soft_purge(Mod), - code:delete(Mod), - false = code:is_loaded(Mod) - end || Mod <- Mods], - application:unload(App) - end || App <- Apps] - end. - -run_cleanup_steps(Apps) -> - Completed = sets:new(), - lists:foldl( - fun({_, Name, _}=Step, Acc) -> - case sets:is_element(Name, Completed) of - true -> Acc; - false -> run_cleanup_step(Step), - sets:add_element(Name, Acc) - end - end, - Completed, - [Step || {App, _, _}=Step <- rabbit:load_steps(cleanup), - lists:member(App, Apps)]), - ok. - -%%--------------------------------------------------------------------------- -%% Private API - -force_reload(Apps) -> - ok = app_utils:load_applications(Apps), - ok = do_reload(Apps). - -do_reload([App|Apps]) -> - case application:get_key(App, modules) of - {ok, Modules} -> reload_all(Modules); - _ -> ok - end, - force_reload(Apps); -do_reload([]) -> - ok. - -reload_all(Modules) -> - [begin - case code:soft_purge(Mod) of - true -> load_mod(Mod); - false -> ok - end - end || Mod <- Modules]. - -load_mod(Mod) -> - case code:is_loaded(Mod) of - {file, Path} when Path /= 'preloaded' -> code:load_abs(Path); - _ -> code:load_file(Mod) - end. - -run_cleanup_step({_, StepName, Attributes}) -> - rabbit:run_step(StepName, Attributes, cleanup). - -already_run(StepName) -> - ets:member(?MODULE, StepName). - -mark_complete(StepName) -> - ets:insert(?MODULE, {StepName}). - diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 6572a625bd..1f36ce4ce0 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -48,7 +48,7 @@ enable(Plugins) -> disable(Plugins) -> app_utils:update_running_apps( - fun() -> rabbit_boot:stop(Plugins) end, + fun() -> rabbit:stop_apps(Plugins) end, fun(Diff) -> ok = rabbit_event:notify(plugins_changed, [{disabled, Diff}]) end). |
