From 04811f9b349e548bf030cd9797eaf6034a6e4229 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Nov 2013 14:50:33 +0000 Subject: Restructure our boot procedure for more flexible boot-step handling --- src/app_utils.erl | 20 +++-- src/rabbit.erl | 136 ++---------------------------- src/rabbit_alarm.erl | 6 +- src/rabbit_boot.erl | 210 ++++++++++++++++++++++++++++++++++++++++++++++ src/rabbit_misc.erl | 16 ++-- src/rabbit_networking.erl | 4 +- 6 files changed, 245 insertions(+), 147 deletions(-) create mode 100644 src/rabbit_boot.erl diff --git a/src/app_utils.erl b/src/app_utils.erl index 5ae2d295..24c6165e 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -17,7 +17,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, - wait_for_applications/1]). + wait_for_applications/1, app_dependencies/1]). -ifdef(use_specs). @@ -30,6 +30,7 @@ -spec stop_applications([atom()], error_handler()) -> 'ok'. -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. +-spec app_dependencies(atom()) -> [atom()]. -endif. @@ -54,7 +55,7 @@ stop_applications(Apps) -> start_applications(Apps, ErrorHandler) -> manage_applications(fun lists:foldl/3, - fun application:start/1, + fun start/1, fun application:stop/1, already_started, ErrorHandler, @@ -68,7 +69,6 @@ stop_applications(Apps, ErrorHandler) -> ErrorHandler, Apps). - wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. @@ -80,8 +80,9 @@ app_dependency_order(RootApps, StripUnreachable) -> {App, _Desc, _Vsn} <- application:loaded_applications()]), try case StripUnreachable of - true -> digraph:del_vertices(G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)); + true -> digraph:del_vertices( + G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)); false -> ok end, digraph_utils:topsort(G) @@ -92,6 +93,15 @@ app_dependency_order(RootApps, StripUnreachable) -> %%--------------------------------------------------------------------------- %% Private API +start(rabbit) -> + case application:start(rabbit) of + ok -> rabbit_boot:run_boot_steps(rabbit), ok; + Err -> Err + end; +start(App) -> + rabbit_boot:run_boot_steps(App), + application:start(App). + wait_for_application(Application) -> case lists:keymember(Application, 1, rabbit_misc:which_applications()) of true -> ok; diff --git a/src/rabbit.erl b/src/rabbit.erl index 1b7fe6da..562497b3 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -312,8 +312,7 @@ start() -> ok = ensure_working_log_handlers(), rabbit_node_monitor:prepare_cluster_status_files(), rabbit_mnesia:check_cluster_consistency(), - ok = app_utils:start_applications( - app_startup_order(), fun handle_app_error/2), + ok = rabbit_boot:start(app_startup_order()), ok = log_broker_started(rabbit_plugins:active()) end). @@ -331,20 +330,10 @@ boot() -> rabbit_mnesia:check_cluster_consistency(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, - ok = app_utils:load_applications(ToBeLoaded), - StartupApps = app_utils:app_dependency_order(ToBeLoaded, - false), - ok = app_utils:start_applications( - StartupApps, fun handle_app_error/2), + ok = rabbit_boot:start(ToBeLoaded), 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}). - start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), register(rabbit_boot, Marker), @@ -352,9 +341,9 @@ start_it(StartFun) -> StartFun() catch throw:{could_not_start, _App, _Reason}=Err -> - boot_error(Err, not_available); + rabbit_boot:boot_error(Err, not_available); _:Reason -> - boot_error(Reason, erlang:get_stacktrace()) + rabbit_boot:boot_error(Reason, erlang:get_stacktrace()) after unlink(Marker), Marker ! stop, @@ -367,7 +356,7 @@ stop() -> undefined -> ok; _ -> await_startup() end, - rabbit_log:info("Stopping RabbitMQ~n"), + rabbit_log:info("Stopping RabbitMQ~n"), %% TODO: move this to boot:stop/1 ok = app_utils:stop_applications(app_shutdown_order()). stop_and_halt() -> @@ -441,7 +430,6 @@ start(normal, []) -> true = register(rabbit, self()), print_banner(), log_banner(), - [ok = run_boot_step(Step) || Step <- boot_steps()], {ok, SupPid}; Error -> Error @@ -466,120 +454,6 @@ app_shutdown_order() -> Apps = ?APPS ++ rabbit_plugins:active(), app_utils:app_dependency_order(Apps, true). -%%--------------------------------------------------------------------------- -%% boot step logic - -run_boot_step({_StepName, Attributes}) -> - case [MFA || {mfa, MFA} <- Attributes] of - [] -> - ok; - MFAs -> - [try - apply(M,F,A) - of - ok -> ok; - {error, Reason} -> boot_error(Reason, not_available) - catch - _:Reason -> boot_error(Reason, erlang:get_stacktrace()) - end || {M,F,A} <- MFAs], - ok - end. - -boot_steps() -> - sort_boot_steps(rabbit_misc:all_module_attributes(rabbit_boot_step)). - -vertices(_Module, Steps) -> - [{StepName, {StepName, Atts}} || {StepName, Atts} <- Steps]. - -edges(_Module, Steps) -> - [case Key of - requires -> {StepName, OtherStep}; - enables -> {OtherStep, StepName} - end || {StepName, Atts} <- Steps, - {Key, OtherStep} <- Atts, - Key =:= requires orelse Key =:= enables]. - -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 - %% 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}} || - {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 diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index cd1d125b..ac2fb42f 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -53,7 +53,8 @@ start_link() -> start() -> ok = rabbit_sup:start_restartable_child(?MODULE), ok = gen_event:add_handler(?SERVER, ?MODULE, []), - {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), + {ok, MemoryWatermark} = application:get_env(rabbit, + vm_memory_high_watermark), rabbit_sup:start_restartable_child( vm_memory_monitor, [MemoryWatermark, fun (Alarm) -> @@ -61,7 +62,8 @@ start() -> set_alarm(Alarm) end, fun clear_alarm/1]), - {ok, DiskLimit} = application:get_env(disk_free_limit), + {ok, DiskLimit} = application:get_env(rabbit, + disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl new file mode 100644 index 00000000..54263014 --- /dev/null +++ b/src/rabbit_boot.erl @@ -0,0 +1,210 @@ +%% 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([start/1, stop/1]). +-export([run_boot_steps/1]). +-export([boot_error/2, boot_error/4]). + +-ifdef(use_specs). + +-spec(start/1 :: ([atom()]) -> 'ok'). +-spec(stop/1 :: ([atom()]) -> 'ok'). +-spec(run_boot_steps/1 :: (atom()) -> 'ok'). +-spec(boot_error/2 :: (term(), not_available | [tuple()]) -> no_return()). +-spec(boot_error/4 :: (term(), string(), [any()], not_available | [tuple()]) + -> no_return()). + +-endif. + +%%--------------------------------------------------------------------------- +%% Public API + +start(Apps) -> + try + ets:new(boot_steps, [named_table, public, ordered_set]), + ok = app_utils:load_applications(Apps), + StartupApps = app_utils:app_dependency_order(Apps, false), + ok = app_utils:start_applications(StartupApps, + handle_app_error(could_not_start)) + after + ets:delete(boot_steps) + end. + +stop(Apps) -> + ShutdownApps = app_utils:app_dependency_order(Apps, true), + try + ok = app_utils:stop_applications( + ShutdownApps, handle_app_error(error_during_shutdown)) + after + [begin + Steps = + sort_boot_steps(rabbit_misc:all_module_attributes( + App, rabbit_cleanup_step)), + [run_boot_step(Step) || Step <- Steps] + end || App <- ShutdownApps] + end. + +run_boot_steps(App) -> + RootApps = app_utils:app_dependencies(App), + Steps = + sort_boot_steps( + lists:usort( + lists:append( + [rabbit_misc:all_module_attributes(A, rabbit_boot_step) || + A <- [App|RootApps]]))), + [ok = run_boot_step(Step) || Step <- Steps], + ok. + +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 + +handle_app_error(Term) -> + fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> + throw({Term, App, ExitReason}); + (App, Reason) -> + throw({Term, App, Reason}) + end. + +run_boot_step({StepName, Attributes}) -> + case already_run(StepName) of + false -> run_it(StepName, Attributes); + true -> ok + end. + +run_it(StepName, Attributes) -> + case [MFA || {mfa, MFA} <- Attributes] of + [] -> + ok; + MFAs -> + [try + apply(M,F,A) + of + ok -> mark_complete(StepName); + {error, Reason} -> boot_error(Reason, not_available) + catch + _:Reason -> boot_error(Reason, erlang:get_stacktrace()) + end || {M,F,A} <- MFAs], + ok + end. + +already_run(StepName) -> + ets:member(boot_steps, StepName). + +mark_complete(StepName) -> + ets:insert(boot_steps, {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, Steps) -> + [{StepName, {StepName, Atts}} || {StepName, Atts} <- Steps]. + +edges(_Module, Steps) -> + [case Key of + requires -> {StepName, OtherStep}; + enables -> {OtherStep, StepName} + end || {StepName, Atts} <- Steps, + {Key, OtherStep} <- Atts, + Key =:= requires orelse Key =:= enables]. + +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 + %% 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}} || + {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. + + diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 00c4eaf3..0fa3190a 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -51,7 +51,8 @@ -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). -export([gb_trees_fold/3, gb_trees_foreach/2]). -export([parse_arguments/3]). --export([all_module_attributes/1, build_acyclic_graph/3]). +-export([all_module_attributes/1, all_module_attributes/2]). +-export([build_acyclic_graph/3]). -export([now_ms/0]). -export([const_ok/0, const/1]). -export([ntoa/1, ntoab/1]). @@ -209,6 +210,7 @@ -> {'ok', {atom(), [{string(), string()}], [string()]}} | 'no_command'). -spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]). +-spec(all_module_attributes/2 :: (atom(), atom()) -> [{atom(), [term()]}]). -spec(build_acyclic_graph/3 :: (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) -> rabbit_types:ok_or_error2(digraph(), @@ -850,12 +852,8 @@ module_attributes(Module) -> V end. -all_module_attributes(Name) -> - Modules = - lists:usort( - lists:append( - [Modules || {App, _, _} <- application:loaded_applications(), - {ok, Modules} <- [application:get_key(App, modules)]])), +all_module_attributes(App, Name) -> + {ok, Modules} = application:get_key(App, modules), lists:foldl( fun (Module, Acc) -> case lists:append([Atts || {N, Atts} <- module_attributes(Module), @@ -865,6 +863,10 @@ all_module_attributes(Name) -> end end, [], Modules). +all_module_attributes(Name) -> + lists:usort(lists:append( + [all_module_attributes(App, Name) || + {App, _, _} <- application:loaded_applications()])). build_acyclic_graph(VertexFun, EdgeFun, Graph) -> G = digraph:new([acyclic]), diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 91be4dcb..5960c000 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -125,12 +125,12 @@ boot() -> ok = boot_ssl(). boot_tcp() -> - {ok, TcpListeners} = application:get_env(tcp_listeners), + {ok, TcpListeners} = application:get_env(rabbit, tcp_listeners), [ok = start_tcp_listener(Listener) || Listener <- TcpListeners], ok. boot_ssl() -> - case application:get_env(ssl_listeners) of + case application:get_env(rabbit, ssl_listeners) of {ok, []} -> ok; {ok, SslListeners} -> -- cgit v1.2.1 From 61266a5e641c7a4d67e348de48081f394290079b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Nov 2013 15:10:36 +0000 Subject: Allow for runtime disabling of plugins --- scripts/rabbitmq-plugins | 2 ++ src/rabbit_boot.erl | 1 + src/rabbit_plugins.erl | 14 +++++++++++- src/rabbit_plugins_main.erl | 53 +++++++++++++++++++++++++++++++-------------- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins index 90eb5a5d..c6f3ff9c 100755 --- a/scripts/rabbitmq-plugins +++ b/scripts/rabbitmq-plugins @@ -21,6 +21,7 @@ ##--- Set environment vars RABBITMQ_ to defaults if not set +[ "x" = "x$RABBITMQ_NODENAME" ] && RABBITMQ_NODENAME=${NODENAME} [ "x" = "x$RABBITMQ_ENABLED_PLUGINS_FILE" ] && RABBITMQ_ENABLED_PLUGINS_FILE=${ENABLED_PLUGINS_FILE} [ "x" = "x$RABBITMQ_PLUGINS_DIR" ] && RABBITMQ_PLUGINS_DIR=${PLUGINS_DIR} @@ -35,4 +36,5 @@ exec ${ERL_DIR}erl \ -s rabbit_plugins_main \ -enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \ -plugins_dist_dir "$RABBITMQ_PLUGINS_DIR" \ + -nodename $RABBITMQ_NODENAME \ -extra "$@" diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 54263014..ffc74ca0 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -47,6 +47,7 @@ start(Apps) -> stop(Apps) -> ShutdownApps = app_utils:app_dependency_order(Apps, true), + io:format("Stopping ~p~n", [ShutdownApps]), try ok = app_utils:stop_applications( ShutdownApps, handle_app_error(error_during_shutdown)) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 168ced3c..81cebd99 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -18,6 +18,7 @@ -include("rabbit.hrl"). -export([setup/0, active/0, read_enabled/1, list/1, dependencies/3]). +-export([enable/1, disable/1]). %%---------------------------------------------------------------------------- @@ -31,11 +32,22 @@ -spec(read_enabled/1 :: (file:filename()) -> [plugin_name()]). -spec(dependencies/3 :: (boolean(), [plugin_name()], [#plugin{}]) -> [plugin_name()]). - +-spec(enable/1 :: ([plugin_name()]) -> 'ok'). +-spec(disable/1 :: ([plugin_name()]) -> 'ok'). -endif. %%---------------------------------------------------------------------------- +%% TODO: move all the {enable,disable}ment logic from plugins_main to here! + +enable(Plugins) -> + setup(), + rabbit_boot:start(Plugins). + +disable(Plugins) -> + setup(), + rabbit_boot:stop(Plugins). + %% @doc Prepares the file system and installs all enabled plugins. setup() -> {ok, PluginDir} = application:get_env(rabbit, plugins_dir), diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 948d2ab0..5c0190e9 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -19,17 +19,21 @@ -export([start/0, stop/0]). +-define(NODE_OPT, "-n"). -define(VERBOSE_OPT, "-v"). -define(MINIMAL_OPT, "-m"). -define(ENABLED_OPT, "-E"). -define(ENABLED_ALL_OPT, "-e"). +-define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). -define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). -define(ENABLED_DEF, {?ENABLED_OPT, flag}). -define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). --define(GLOBAL_DEFS, []). +-define(RPC_TIMEOUT, infinity). + +-define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]). -define(COMMANDS, [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, @@ -51,11 +55,10 @@ start() -> {ok, [[PluginsFile|_]|_]} = init:get_argument(enabled_plugins_file), + {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), {ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir), {Command, Opts, Args} = - case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS, - init:get_plain_arguments()) - of + case parse_arguments(init:get_plain_arguments(), NodeStr) of {ok, Res} -> Res; no_command -> print_error("could not recognise command", []), usage() @@ -67,7 +70,8 @@ start() -> [string:join([atom_to_list(Command) | Args], " ")]) end, - case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of + Node = proplists:get_value(?NODE_OPT, Opts), + case catch action(Command, Node, Args, Opts, PluginsFile, PluginsDir) of ok -> rabbit_misc:quit(0); {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> @@ -92,12 +96,25 @@ stop() -> %%---------------------------------------------------------------------------- -action(list, [], Opts, PluginsFile, PluginsDir) -> - action(list, [".*"], Opts, PluginsFile, PluginsDir); -action(list, [Pat], Opts, PluginsFile, PluginsDir) -> +parse_arguments(CmdLine, NodeStr) -> + case rabbit_misc:parse_arguments( + ?COMMANDS, ?GLOBAL_DEFS(NodeStr), CmdLine) of + {ok, {Cmd, Opts0, Args}} -> + Opts = [case K of + ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; + _ -> {K, V} + end || {K, V} <- Opts0], + {ok, {Cmd, Opts, Args}}; + E -> + E + end. + +action(list, Node, [], Opts, PluginsFile, PluginsDir) -> + action(list, Node, [".*"], Opts, PluginsFile, PluginsDir); +action(list, _Node, [Pat], Opts, PluginsFile, PluginsDir) -> format_plugins(Pat, Opts, PluginsFile, PluginsDir); -action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> +action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> case ToEnable0 of [] -> throw({error_string, "Not enough arguments for 'enable'"}); _ -> ok @@ -125,10 +142,10 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> [] -> io:format("Plugin configuration unchanged.~n"); _ -> print_list("The following plugins have been enabled:", NewImplicitlyEnabled -- ImplicitlyEnabled), - report_change() + action_change(Node, enable, NewEnabled) end; -action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> +action(disable, Node, ToDisable0, _Opts, PluginsFile, PluginsDir) -> case ToDisable0 of [] -> throw({error_string, "Not enough arguments for 'disable'"}); _ -> ok @@ -151,10 +168,11 @@ action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), + Disabled = ImplicitlyEnabled -- NewImplicitlyEnabled, print_list("The following plugins have been disabled:", - ImplicitlyEnabled -- NewImplicitlyEnabled), + Disabled), write_enabled_plugins(PluginsFile, NewEnabled), - report_change() + action_change(Node, disable, Disabled) end. %%---------------------------------------------------------------------------- @@ -262,6 +280,9 @@ write_enabled_plugins(PluginsFile, Plugins) -> PluginsFile, Reason}}) end. -report_change() -> - io:format("Plugin configuration has changed. " - "Restart RabbitMQ for changes to take effect.~n"). +action_change(Node, Action, Targets) -> + rpc_call(Node, rabbit_plugins, Action, [Targets]). + +rpc_call(Node, Mod, Fun, Args) -> + rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). + -- cgit v1.2.1 From be00641ced64b26e80869a79f6d830b2aa45c9fe Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Nov 2013 15:13:03 +0000 Subject: Ensure we pass the nodename on windows --- scripts/rabbitmq-plugins.bat | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 0d1f128e..252ecb3a 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -31,6 +31,10 @@ if "!RABBITMQ_BASE!"=="" ( set RABBITMQ_BASE=!APPDATA!\!RABBITMQ_SERVICENAME! ) +if "!RABBITMQ_NODENAME!"=="" ( + set RABBITMQ_NODENAME=rabbit@!COMPUTERNAME! +) + if not exist "!ERLANG_HOME!\bin\erl.exe" ( echo. echo ****************************** @@ -51,7 +55,7 @@ if "!RABBITMQ_PLUGINS_DIR!"=="" ( set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins ) -"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM!!TIME:~9! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! +"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM!!TIME:~9! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -nodename !RABBITMQ_NODENAME! -extra !STAR! endlocal endlocal -- cgit v1.2.1 From fa48bba1c5df3b5283fd76daabeadea8e6da4fbd Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Nov 2013 16:30:40 +0000 Subject: Allow offline plugin re-configuration --- src/rabbit_plugins_main.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 5c0190e9..b7222c1e 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -284,5 +284,8 @@ action_change(Node, Action, Targets) -> rpc_call(Node, rabbit_plugins, Action, [Targets]). rpc_call(Node, Mod, Fun, Args) -> - rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). + case rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT) of + {badrpc, nodedown} -> io:format("Plugin configuration has changed.~n"); + _ -> ok + end. -- cgit v1.2.1 From 34127a2ab633e8d54acc67bab24d802cd50d9798 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 6 Nov 2013 11:51:07 +0000 Subject: track boot status throughout a node's lifetime --- src/rabbit.erl | 80 +++++++++++++++++++------------------------------ src/rabbit_boot.erl | 86 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 111 insertions(+), 55 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 562497b3..74efbf22 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -305,59 +305,40 @@ ensure_application_loaded() -> end. start() -> - start_it(fun() -> - %% We do not want to HiPE compile or upgrade - %% mnesia after just restarting the app - ok = ensure_application_loaded(), - ok = ensure_working_log_handlers(), - rabbit_node_monitor:prepare_cluster_status_files(), - rabbit_mnesia:check_cluster_consistency(), - ok = rabbit_boot:start(app_startup_order()), - ok = log_broker_started(rabbit_plugins:active()) - end). + rabbit_boot:boot_with( + fun() -> + %% We do not want to HiPE compile or upgrade + %% mnesia after just restarting the app + ok = ensure_application_loaded(), + ok = ensure_working_log_handlers(), + rabbit_node_monitor:prepare_cluster_status_files(), + rabbit_mnesia:check_cluster_consistency(), + ok = rabbit_boot:start(app_startup_order()), + ok = log_broker_started(rabbit_plugins:active()) + end). boot() -> - start_it(fun() -> - ok = ensure_application_loaded(), - Success = maybe_hipe_compile(), - ok = ensure_working_log_handlers(), - warn_if_hipe_compilation_failed(Success), - rabbit_node_monitor:prepare_cluster_status_files(), - ok = rabbit_upgrade:maybe_upgrade_mnesia(), - %% It's important that the consistency check happens after - %% the upgrade, since if we are a secondary node the - %% primary node will have forgotten us - rabbit_mnesia:check_cluster_consistency(), - Plugins = rabbit_plugins:setup(), - ToBeLoaded = Plugins ++ ?APPS, - ok = rabbit_boot:start(ToBeLoaded), - ok = log_broker_started(Plugins) - end). - -start_it(StartFun) -> - Marker = spawn_link(fun() -> receive stop -> ok end end), - register(rabbit_boot, Marker), - try - StartFun() - catch - throw:{could_not_start, _App, _Reason}=Err -> - rabbit_boot:boot_error(Err, not_available); - _:Reason -> - rabbit_boot:boot_error(Reason, erlang:get_stacktrace()) - after - unlink(Marker), - Marker ! stop, - %% give the error loggers some time to catch up - timer:sleep(100) - end. + rabbit_boot:boot_with( + fun() -> + ok = ensure_application_loaded(), + Success = maybe_hipe_compile(), + ok = ensure_working_log_handlers(), + warn_if_hipe_compilation_failed(Success), + rabbit_node_monitor:prepare_cluster_status_files(), + ok = rabbit_upgrade:maybe_upgrade_mnesia(), + %% It's important that the consistency check happens after + %% the upgrade, since if we are a secondary node the + %% primary node will have forgotten us + rabbit_mnesia:check_cluster_consistency(), + Plugins = rabbit_plugins:setup(), + ToBeLoaded = Plugins ++ ?APPS, + ok = rabbit_boot:start(ToBeLoaded), + ok = log_broker_started(Plugins) + end). stop() -> - case whereis(rabbit_boot) of - undefined -> ok; - _ -> await_startup() - end, - rabbit_log:info("Stopping RabbitMQ~n"), %% TODO: move this to boot:stop/1 - ok = app_utils:stop_applications(app_shutdown_order()). + rabbit_log:info("Stopping RabbitMQ~n"), + rabbit_boot:shutdown(app_shutdown_order()). stop_and_halt() -> try @@ -441,6 +422,7 @@ stop(_State) -> true -> rabbit_amqqueue:on_node_down(node()); false -> rabbit_table:clear_ram_only_tables() end, + ok = rabbit_boot:shutdown(), ok. %%--------------------------------------------------------------------------- diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index ffc74ca0..7de74b14 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -16,38 +16,73 @@ -module(rabbit_boot). +-export([boot_with/1, shutdown/1]). -export([start/1, stop/1]). -export([run_boot_steps/1]). -export([boot_error/2, boot_error/4]). -ifdef(use_specs). +-spec(boot_with/1 :: (fun(() -> 'ok')) -> 'ok'). +-spec(shutdown/1 :: ([atom()]) -> 'ok'). -spec(start/1 :: ([atom()]) -> 'ok'). -spec(stop/1 :: ([atom()]) -> 'ok'). -spec(run_boot_steps/1 :: (atom()) -> 'ok'). -spec(boot_error/2 :: (term(), not_available | [tuple()]) -> no_return()). -spec(boot_error/4 :: (term(), string(), [any()], not_available | [tuple()]) - -> no_return()). + -> no_return()). -endif. +-define(BOOT_FILE, "boot.info"). + %%--------------------------------------------------------------------------- %% Public API +boot_with(StartFun) -> + %% TODO: this should be done with monitors, not links, I think + Marker = spawn_link(fun() -> receive stop -> ok end end), + register(rabbit_boot, Marker), + try + StartFun() + 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. + +shutdown(Apps) -> + try + case whereis(rabbit_boot) of + undefined -> ok; + _ -> await_startup(Apps) + end, + rabbit_log:info("Stopping RabbitMQ~n"), + ok = app_utils:stop_applications(Apps) + after + delete_boot_table() + end. + start(Apps) -> try - ets:new(boot_steps, [named_table, public, ordered_set]), + ensure_boot_table(), ok = app_utils:load_applications(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), ok = app_utils:start_applications(StartupApps, handle_app_error(could_not_start)) after - ets:delete(boot_steps) + save_boot_table() end. stop(Apps) -> + %% ensure_boot_table(), ShutdownApps = app_utils:app_dependency_order(Apps, true), - io:format("Stopping ~p~n", [ShutdownApps]), try ok = app_utils:stop_applications( ShutdownApps, handle_app_error(error_during_shutdown)) @@ -99,6 +134,45 @@ boot_error(Reason, Fmt, Args, Stacktrace) -> %%--------------------------------------------------------------------------- %% Private API +await_startup(Apps) -> + app_utils:wait_for_applications(Apps). + +delete_boot_table() -> + case filelib:is_file(boot_file()) of + true -> file:delete(boot_file()); + false -> ok + end. + +ensure_boot_table() -> + case whereis(?MODULE) of + undefined -> + case filelib:is_file(boot_file()) of + true -> load_table(); + false -> clean_table() + end; + _Pid -> + clean_table() + end. + +clean_table() -> + ets:new(?MODULE, [named_table, public, ordered_set]). + +load_table() -> + {ok, _Tab} = ets:file2tab(boot_file(), [{verify, true}]), + ok. + +save_boot_table() -> + delete_boot_table(), + case ets:info(?MODULE) of + undefined -> ok; + _ -> ets:tab2file(?MODULE, boot_file(), + [{extended_info, [object_count]}]), + ets:delete(?MODULE) + end. + +boot_file() -> + filename:join(rabbit_mnesia:dir(), ?BOOT_FILE). + handle_app_error(Term) -> fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> throw({Term, App, ExitReason}); @@ -129,10 +203,10 @@ run_it(StepName, Attributes) -> end. already_run(StepName) -> - ets:member(boot_steps, StepName). + ets:member(?MODULE, StepName). mark_complete(StepName) -> - ets:insert(boot_steps, {StepName}). + ets:insert(?MODULE, {StepName}). basic_boot_error(Reason, Format, Args) -> io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args), -- cgit v1.2.1 From 32c17dbdbb989d7b080afff0e675b89895706851 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 6 Nov 2013 14:55:12 +0000 Subject: handle cleanup steps properly --- src/rabbit_boot.erl | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 7de74b14..9df2edc3 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -81,29 +81,33 @@ start(Apps) -> end. stop(Apps) -> - %% ensure_boot_table(), + ensure_boot_table(), ShutdownApps = app_utils:app_dependency_order(Apps, true), try ok = app_utils:stop_applications( ShutdownApps, handle_app_error(error_during_shutdown)) after - [begin - Steps = - sort_boot_steps(rabbit_misc:all_module_attributes( - App, rabbit_cleanup_step)), - [run_boot_step(Step) || Step <- Steps] - end || App <- ShutdownApps] + [run_steps(App, rabbit_cleanup_step) || App <- ShutdownApps] + %[begin + % Steps = + % sort_boot_steps(rabbit_misc:all_module_attributes( + % App, rabbit_cleanup_step)), + % [run_boot_step(Step) || Step <- Steps] + % end || App <- ShutdownApps] end. run_boot_steps(App) -> + run_steps(App, rabbit_boot_step). + +run_steps(App, StepType) -> RootApps = app_utils:app_dependencies(App), Steps = sort_boot_steps( lists:usort( lists:append( - [rabbit_misc:all_module_attributes(A, rabbit_boot_step) || + [rabbit_misc:all_module_attributes(A, StepType) || A <- [App|RootApps]]))), - [ok = run_boot_step(Step) || Step <- Steps], + [ok = run_boot_step(Step, StepType) || Step <- Steps], ok. boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> @@ -180,10 +184,11 @@ handle_app_error(Term) -> throw({Term, App, Reason}) end. -run_boot_step({StepName, Attributes}) -> - case already_run(StepName) of - false -> run_it(StepName, Attributes); - true -> ok +run_boot_step({StepName, Attributes}, StepType) -> + case catch {StepType, already_run(StepName)} of + {rabbit_clean_step, _} -> run_it(StepName, Attributes); + {_, false} -> run_it(StepName, Attributes); + {_, true} -> ok end. run_it(StepName, Attributes) -> -- cgit v1.2.1 From 1417b7293c041689e8a905d222804836a8ccdec5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 18 Nov 2013 12:12:05 +0000 Subject: Rework boot step handling --- src/app_utils.erl | 5 ++++- src/rabbit_boot.erl | 17 ++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 24c6165e..1231ea58 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -17,7 +17,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, - wait_for_applications/1, app_dependencies/1]). + running_applications/0, wait_for_applications/1, app_dependencies/1]). -ifdef(use_specs). @@ -37,6 +37,9 @@ %%--------------------------------------------------------------------------- %% Public API +running_applications() -> + [App || {App, _, _} <- application:which_applications()]. + load_applications(Apps) -> load_applications(queue:from_list(Apps), sets:new()), ok. diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 9df2edc3..58361e26 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -82,18 +82,21 @@ start(Apps) -> stop(Apps) -> ensure_boot_table(), - ShutdownApps = app_utils:app_dependency_order(Apps, true), + TargetApps = lists:usort([app_utils:app_dependency_order(App, true) || + InitApp <- Apps, + App <- app_utils:app_dependencies(InitApp)]), + Ineligible = lists:usort( + app_utils:app_dependency_order( + [App || App <- app_utils:running_applications(), + not lists:member(App, Apps)], + true) ++ [rabbit]), + ShutdownApps = TargetApps -- Ineligible, + io:format("Shutdown apps = ~p~n", [ShutdownApps]), try ok = app_utils:stop_applications( ShutdownApps, handle_app_error(error_during_shutdown)) after [run_steps(App, rabbit_cleanup_step) || App <- ShutdownApps] - %[begin - % Steps = - % sort_boot_steps(rabbit_misc:all_module_attributes( - % App, rabbit_cleanup_step)), - % [run_boot_step(Step) || Step <- Steps] - % end || App <- ShutdownApps] end. run_boot_steps(App) -> -- cgit v1.2.1 From 4d5ac08b57dd4ac2f68244f3c6a08d96d8551cbc Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 20 Nov 2013 12:13:05 +0000 Subject: Various boot procedure improvements We ensure that all dependent applications are stopped, but also that dependencies shared by multiple applications (e.g., amqp_client) are not. --- src/app_utils.erl | 36 ++++++++++++++++++++++++++++++++++++ src/rabbit.erl | 10 +++++----- src/rabbit_boot.erl | 47 +++++++++++++++++++++++++---------------------- src/rabbit_misc.erl | 35 +++++++++++++++++++++++++---------- 4 files changed, 91 insertions(+), 37 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 1231ea58..d08dfe6a 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -18,6 +18,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, running_applications/0, wait_for_applications/1, app_dependencies/1]). +-export([direct_dependencies/1]). -ifdef(use_specs). @@ -31,6 +32,7 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. +-spec direct_dependencies(atom()) -> [atom()]. -endif. @@ -75,6 +77,40 @@ stop_applications(Apps, ErrorHandler) -> wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. +direct_dependencies(Root) -> + G = digraph:new(), + Loaded = application:loaded_applications(), + try + [begin + case digraph:vertex(G, App) of + false -> digraph:add_vertex(G, App, App); + _ -> ok = throw({graph_error, {vertex, duplicate, App}}) + end + end || {App, _, _} <- Loaded], + [digraph:add_edge(G, App, Dep) || + {App, Deps} <- [{App, app_dependencies(App)} || + {App, _Desc, _Vsn} <- Loaded], + Dep <- Deps], + Deps = lists:foldl( + fun(E, Acc) -> + {_, _InVertex, OutVertex, _Label} = digraph:edge(G, E), + case is_reachable(G, OutVertex, Root) of + [] -> sets:add_element(OutVertex, Acc); + _ -> Acc + end + end, + sets:from_list([Root]), + digraph:out_edges(G, Root)), + sets:to_list(sets:del_element(rabbit, Deps)) + catch {graph_error, Reason} -> + {error, Reason} + after + true = digraph:delete(G) + end. + +is_reachable(G, OutVertex, Root) -> + digraph_utils:reaching_neighbours([OutVertex], G) -- [Root]. + app_dependency_order(RootApps, StripUnreachable) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, diff --git a/src/rabbit.erl b/src/rabbit.erl index 74efbf22..44cd275d 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -457,12 +457,12 @@ maybe_insert_default_data() -> end. insert_default_data() -> - {ok, DefaultUser} = application:get_env(default_user), - {ok, DefaultPass} = application:get_env(default_pass), - {ok, DefaultTags} = application:get_env(default_user_tags), - {ok, DefaultVHost} = application:get_env(default_vhost), + {ok, DefaultUser} = application:get_env(rabbit, default_user), + {ok, DefaultPass} = application:get_env(rabbit, default_pass), + {ok, DefaultTags} = application:get_env(rabbit, default_user_tags), + {ok, DefaultVHost} = application:get_env(rabbit, default_vhost), {ok, [DefaultConfigurePerm, DefaultWritePerm, DefaultReadPerm]} = - application:get_env(default_permissions), + application:get_env(rabbit, default_permissions), ok = rabbit_vhost:add(DefaultVHost), ok = rabbit_auth_backend_internal:add_user(DefaultUser, DefaultPass), ok = rabbit_auth_backend_internal:set_tags(DefaultUser, DefaultTags), diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 58361e26..af2bc9c4 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -63,7 +63,8 @@ shutdown(Apps) -> undefined -> ok; _ -> await_startup(Apps) end, - rabbit_log:info("Stopping RabbitMQ~n"), + %% TODO: put this back in somewhere sensible... + %% rabbit_log:info("Stopping RabbitMQ~n"), ok = app_utils:stop_applications(Apps) after delete_boot_table() @@ -82,35 +83,32 @@ start(Apps) -> stop(Apps) -> ensure_boot_table(), - TargetApps = lists:usort([app_utils:app_dependency_order(App, true) || - InitApp <- Apps, - App <- app_utils:app_dependencies(InitApp)]), - Ineligible = lists:usort( - app_utils:app_dependency_order( - [App || App <- app_utils:running_applications(), - not lists:member(App, Apps)], - true) ++ [rabbit]), - ShutdownApps = TargetApps -- Ineligible, - io:format("Shutdown apps = ~p~n", [ShutdownApps]), + TargetApps = + lists:usort( + lists:append([app_utils:direct_dependencies(App) || App <- Apps])), try ok = app_utils:stop_applications( - ShutdownApps, handle_app_error(error_during_shutdown)) + TargetApps, handle_app_error(error_during_shutdown)) after - [run_steps(App, rabbit_cleanup_step) || App <- ShutdownApps] + try + [run_steps(App, rabbit_cleanup_step) || App <- TargetApps] + after + save_boot_table() + end end. run_boot_steps(App) -> run_steps(App, rabbit_boot_step). run_steps(App, StepType) -> - RootApps = app_utils:app_dependencies(App), - Steps = + RootApps = [App|app_utils:direct_dependencies(App)], + StepAttrs = rabbit_misc:all_app_module_attributes(StepType), + BootSteps = sort_boot_steps( - lists:usort( - lists:append( - [rabbit_misc:all_module_attributes(A, StepType) || - A <- [App|RootApps]]))), - [ok = run_boot_step(Step, StepType) || Step <- Steps], + lists:usort( + [{Mod, Steps} || {AppName, Mod, Steps} <- StepAttrs, + lists:member(AppName, RootApps)])), + [ok = run_boot_step(Step, StepType) || Step <- BootSteps], ok. boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> @@ -165,8 +163,13 @@ clean_table() -> ets:new(?MODULE, [named_table, public, ordered_set]). load_table() -> - {ok, _Tab} = ets:file2tab(boot_file(), [{verify, true}]), - ok. + case ets:file2tab(boot_file(), [{verify, true}]) of + {error, _} -> error(fuck); + {ok, _Tab} -> ok, + io:format("Starting with pre-loaded boot table:~n~p~n" + "----------------~n", + [ets:tab2list(?MODULE)]) + end. save_boot_table() -> delete_boot_table(), diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 0fa3190a..3f5bee55 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -51,8 +51,8 @@ -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). -export([gb_trees_fold/3, gb_trees_foreach/2]). -export([parse_arguments/3]). --export([all_module_attributes/1, all_module_attributes/2]). --export([build_acyclic_graph/3]). +-export([all_app_module_attributes/1]). +-export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). -export([const_ok/0, const/1]). -export([ntoa/1, ntoab/1]). @@ -210,7 +210,6 @@ -> {'ok', {atom(), [{string(), string()}], [string()]}} | 'no_command'). -spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]). --spec(all_module_attributes/2 :: (atom(), atom()) -> [{atom(), [term()]}]). -spec(build_acyclic_graph/3 :: (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) -> rabbit_types:ok_or_error2(digraph(), @@ -852,21 +851,37 @@ module_attributes(Module) -> V end. -all_module_attributes(App, Name) -> - {ok, Modules} = application:get_key(App, modules), +all_app_module_attributes(Name) -> + Modules = + lists:usort( + lists:flatten( + [{App, Module} || + {App, _, _} <- application:loaded_applications(), + {ok, Modules} <- [application:get_key(App, modules)], + Module <- Modules])), lists:foldl( - fun (Module, Acc) -> + fun ({App, Module}, Acc) -> case lists:append([Atts || {N, Atts} <- module_attributes(Module), N =:= Name]) of [] -> Acc; - Atts -> [{Module, Atts} | Acc] + Atts -> [{App, Module, Atts} | Acc] end end, [], Modules). all_module_attributes(Name) -> - lists:usort(lists:append( - [all_module_attributes(App, Name) || - {App, _, _} <- application:loaded_applications()])). + Modules = + lists:usort( + lists:append( + [Modules || {App, _, _} <- application:loaded_applications(), + {ok, Modules} <- [application:get_key(App, modules)]])), + lists:foldl( + fun (Module, Acc) -> + case lists:append([Atts || {N, Atts} <- module_attributes(Module), + N =:= Name]) of + [] -> Acc; + Atts -> [{Module, Atts} | Acc] + end + end, [], Modules). build_acyclic_graph(VertexFun, EdgeFun, Graph) -> G = digraph:new([acyclic]), -- cgit v1.2.1 From 0576ac9b930c8dbcc11b068b20ff64fde1357948 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 20 Nov 2013 12:24:49 +0000 Subject: Refactor (de-duplicate) module attribute search --- src/rabbit_misc.erl | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 3f5bee55..26071a8a 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -852,36 +852,37 @@ module_attributes(Module) -> end. all_app_module_attributes(Name) -> - Modules = - lists:usort( - lists:flatten( - [{App, Module} || - {App, _, _} <- application:loaded_applications(), - {ok, Modules} <- [application:get_key(App, modules)], - Module <- Modules])), - lists:foldl( + find_module_attributes( + fun(App, Modules) -> + [{App, Module} || Module <- Modules] + end, fun ({App, Module}, Acc) -> case lists:append([Atts || {N, Atts} <- module_attributes(Module), N =:= Name]) of [] -> Acc; Atts -> [{App, Module, Atts} | Acc] end - end, [], Modules). + end). all_module_attributes(Name) -> - Modules = - lists:usort( - lists:append( - [Modules || {App, _, _} <- application:loaded_applications(), - {ok, Modules} <- [application:get_key(App, modules)]])), - lists:foldl( + find_module_attributes( + fun(_App, Modules) -> Modules end, fun (Module, Acc) -> case lists:append([Atts || {N, Atts} <- module_attributes(Module), N =:= Name]) of [] -> Acc; Atts -> [{Module, Atts} | Acc] end - end, [], Modules). + end). + +find_module_attributes(Generator, Fold) -> + Targets = + lists:usort( + lists:append( + [Generator(App, Modules) || + {App, _, _} <- application:loaded_applications(), + {ok, Modules} <- [application:get_key(App, modules)]])), + lists:foldl(Fold, [], Targets). build_acyclic_graph(VertexFun, EdgeFun, Graph) -> G = digraph:new([acyclic]), -- cgit v1.2.1 From b54be485a07a9bf3e96133f4b2c588f8a5b97e88 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 21 Nov 2013 21:59:55 +0000 Subject: Re-work boot step handling We need to support interleaving plugin boot steps with rabbit?s own (internal) ones during the boot/start sequence, and also running them independently during runtime activation. This commit also drops the use of an ets table to track which cleanup steps we?ve run, and simplifies the boot step execution procedures. --- src/app_utils.erl | 16 ++--- src/rabbit.erl | 1 + src/rabbit_boot.erl | 175 +++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 146 insertions(+), 46 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index d08dfe6a..22e2df44 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -60,7 +60,7 @@ stop_applications(Apps) -> start_applications(Apps, ErrorHandler) -> manage_applications(fun lists:foldl/3, - fun start/1, + fun application:start/1, fun application:stop/1, already_started, ErrorHandler, @@ -101,7 +101,7 @@ direct_dependencies(Root) -> end, sets:from_list([Root]), digraph:out_edges(G, Root)), - sets:to_list(sets:del_element(rabbit, Deps)) + sets:to_list(Deps) catch {graph_error, Reason} -> {error, Reason} after @@ -132,14 +132,10 @@ app_dependency_order(RootApps, StripUnreachable) -> %%--------------------------------------------------------------------------- %% Private API -start(rabbit) -> - case application:start(rabbit) of - ok -> rabbit_boot:run_boot_steps(rabbit), ok; - Err -> Err - end; -start(App) -> - rabbit_boot:run_boot_steps(App), - application:start(App). +%% It might be worth documenting this on the plugin author's guide/page. +%% A plugin should expect its boot steps to run /before/ the application +%% is started, and its cleanup steps to run /after/ the application has +%% fully stopped. wait_for_application(Application) -> case lists:keymember(Application, 1, rabbit_misc:which_applications()) of diff --git a/src/rabbit.erl b/src/rabbit.erl index 44cd275d..374fccc3 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -411,6 +411,7 @@ start(normal, []) -> true = register(rabbit, self()), print_banner(), log_banner(), + rabbit_boot:run_boot_steps(), {ok, SupPid}; Error -> Error diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index af2bc9c4..e2174ddd 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -18,7 +18,7 @@ -export([boot_with/1, shutdown/1]). -export([start/1, stop/1]). --export([run_boot_steps/1]). +-export([run_boot_steps/0]). -export([boot_error/2, boot_error/4]). -ifdef(use_specs). @@ -27,7 +27,7 @@ -spec(shutdown/1 :: ([atom()]) -> 'ok'). -spec(start/1 :: ([atom()]) -> 'ok'). -spec(stop/1 :: ([atom()]) -> 'ok'). --spec(run_boot_steps/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()). @@ -36,6 +36,31 @@ -define(BOOT_FILE, "boot.info"). +%% +%% 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 and run them in dependent +%% order. +%% +%% When the broker is already running, we must run all the boot steps for +%% each application/plugin we're starting, plus any other (dependent) steps +%% that have not been run. To achieve this, we process the boot steps from +%% all loaded applications, but skip those that have already been run (i.e., +%% steps that have been run once whilst or since the broker started). +%% +%% Tracking which boot steps have already been run is done via an ets table. +%% Because we frequently find ourselves needing to query this table without +%% the rabbit application running (e.g., during the initial boot sequence +%% and when we've "cold started" a node without any running applications), +%% this table is serialized to a file after each operation. When the node is +%% stopped cleanly, the file is deleted. When a node is in the process of +%% starting, the file is also removed and replaced (since we do not want to +%% track boot steps from a previous incarnation of the node). +%% +%% Cleanup steps... +%% + %%--------------------------------------------------------------------------- %% Public API @@ -43,6 +68,7 @@ boot_with(StartFun) -> %% TODO: this should be done with monitors, not links, I think Marker = spawn_link(fun() -> receive stop -> ok end end), register(rabbit_boot, Marker), + ensure_boot_table(), try StartFun() catch @@ -73,8 +99,13 @@ shutdown(Apps) -> start(Apps) -> try ensure_boot_table(), - ok = app_utils:load_applications(Apps), + force_reload(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), + io:format("App Start Order: ~p~n", [StartupApps]), + case whereis(?MODULE) of + undefined -> run_boot_steps(); + _ -> ok + end, ok = app_utils:start_applications(StartupApps, handle_app_error(could_not_start)) after @@ -84,33 +115,71 @@ start(Apps) -> stop(Apps) -> ensure_boot_table(), TargetApps = - lists:usort( - lists:append([app_utils:direct_dependencies(App) || App <- Apps])), + sets:to_list( + lists:foldl( + fun(App, Set) -> + lists:foldl(fun sets:add_element/2, Set, + app_utils:direct_dependencies(App) -- [rabbit]) + end, sets:new(), Apps)), + io:format("Target Apps = ~p~n", [TargetApps]), try ok = app_utils:stop_applications( TargetApps, handle_app_error(error_during_shutdown)) after try - [run_steps(App, rabbit_cleanup_step) || App <- TargetApps] + io:format("Running Cleanup~n"), + BootSteps = load_steps(rabbit_boot_step), + ToDelete = [Step || {App, _, _}=Step <- BootSteps, + lists:member(App, TargetApps)], + io:format("Boot steps on shutdown: ~p~n", [ToDelete]), + [begin + ets:delete(?MODULE, Step), + io:format("Deleted ~p~n", [Step]) + end || {_, Step, _} <- ToDelete], + io:format("Run cleanup steps~n"), + run_cleanup_steps(TargetApps) after + io:format("save boot table~n"), save_boot_table() - end + end, + [begin + {ok, Mods} = application:get_key(App, modules), + io:format("cleanup ~p...~n", [Mods]), + [begin + code:soft_purge(Mod), + code:delete(Mod), + false = code:is_loaded(Mod) + end || Mod <- Mods], + application:unload(App) + end || App <- TargetApps] end. -run_boot_steps(App) -> - run_steps(App, rabbit_boot_step). +run_cleanup_steps(Apps) -> + Completed = sets:new(), + lists:foldl( + fun({_, Name, _}=Step, Acc) -> + case sets:is_element(Name, Completed) of + true -> Acc; + false -> run_boot_step(Step, rabbit_cleanup_step), + sets:add_element(Name, Acc) + end + end, + Completed, + [Step || {App, _, _}=Step <- load_steps(rabbit_cleanup_step), + lists:member(App, Apps)]), + ok. -run_steps(App, StepType) -> - RootApps = [App|app_utils:direct_dependencies(App)], - StepAttrs = rabbit_misc:all_app_module_attributes(StepType), - BootSteps = - sort_boot_steps( - lists:usort( - [{Mod, Steps} || {AppName, Mod, Steps} <- StepAttrs, - lists:member(AppName, RootApps)])), - [ok = run_boot_step(Step, StepType) || Step <- BootSteps], +run_boot_steps() -> + Steps = load_steps(rabbit_boot_step), + [ok = run_boot_step(Step, rabbit_boot_step) || Step <- Steps], ok. +load_steps(StepType) -> + StepAttrs = rabbit_misc:all_app_module_attributes(StepType), + sort_boot_steps( + 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} = @@ -139,6 +208,34 @@ boot_error(Reason, Fmt, Args, Stacktrace) -> %%--------------------------------------------------------------------------- %% 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 -> io:format("unable to purge ~p~n", [Mod]) + end + end || Mod <- Modules]. + +load_mod(Mod) -> + case code:is_loaded(Mod) of + {file, preloaded} -> code:load_file(Mod); + {file, Path} -> code:load_abs(Path); + false -> code:load_file(Mod) + end. + await_startup(Apps) -> app_utils:wait_for_applications(Apps). @@ -160,15 +257,18 @@ ensure_boot_table() -> end. clean_table() -> - ets:new(?MODULE, [named_table, public, ordered_set]). + delete_boot_table(), + case ets:info(?MODULE) of + undefined -> + ets:new(?MODULE, [named_table, public, ordered_set]); + _ -> + ok + end. load_table() -> case ets:file2tab(boot_file(), [{verify, true}]) of - {error, _} -> error(fuck); - {ok, _Tab} -> ok, - io:format("Starting with pre-loaded boot table:~n~p~n" - "----------------~n", - [ets:tab2list(?MODULE)]) + {error, _} -> clean_table(); + {ok, _Tab} -> ok end. save_boot_table() -> @@ -190,14 +290,17 @@ handle_app_error(Term) -> throw({Term, App, Reason}) end. -run_boot_step({StepName, Attributes}, StepType) -> - case catch {StepType, already_run(StepName)} of - {rabbit_clean_step, _} -> run_it(StepName, Attributes); - {_, false} -> run_it(StepName, Attributes); - {_, true} -> ok - end. +run_boot_step({_, _, Attributes}, rabbit_cleanup_step) -> + run_step(Attributes); +run_boot_step({_, StepName, Attributes}, rabbit_boot_step) -> + case catch already_run(StepName) of + false -> ok = run_step(Attributes), + mark_complete(StepName); + true -> ok + end, + ok. -run_it(StepName, Attributes) -> +run_step(Attributes) -> case [MFA || {mfa, MFA} <- Attributes] of [] -> ok; @@ -205,7 +308,7 @@ run_it(StepName, Attributes) -> [try apply(M,F,A) of - ok -> mark_complete(StepName); + ok -> ok; {error, Reason} -> boot_error(Reason, not_available) catch _:Reason -> boot_error(Reason, erlang:get_stacktrace()) @@ -239,10 +342,10 @@ log_location(Type) -> _ -> undefined end. -vertices(_Module, Steps) -> - [{StepName, {StepName, Atts}} || {StepName, Atts} <- Steps]. +vertices(_Module, {AppName, Steps}) -> + [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps]. -edges(_Module, Steps) -> +edges(_Module, {_AppName, Steps}) -> [case Key of requires -> {StepName, OtherStep}; enables -> {OtherStep, StepName} @@ -265,7 +368,7 @@ sort_boot_steps(UnsortedSteps) -> digraph:delete(G), %% Check that all mentioned {M,F,A} triples are exported. case [{StepName, {M,F,A}} || - {StepName, Attributes} <- SortedSteps, + {_App, StepName, Attributes} <- SortedSteps, {mfa, {M,F,A}} <- Attributes, not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; -- cgit v1.2.1 From f53e302572f905fd67df0f2c2025c5727e8ec970 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 26 Nov 2013 11:29:21 +0000 Subject: Remove lots of io:format debugging and tidy up rpc error messages --- src/rabbit_boot.erl | 12 +----------- src/rabbit_plugins_main.erl | 14 +++++++++++--- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index e2174ddd..d1b32b6a 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -101,7 +101,6 @@ start(Apps) -> ensure_boot_table(), force_reload(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), - io:format("App Start Order: ~p~n", [StartupApps]), case whereis(?MODULE) of undefined -> run_boot_steps(); _ -> ok @@ -121,30 +120,21 @@ stop(Apps) -> lists:foldl(fun sets:add_element/2, Set, app_utils:direct_dependencies(App) -- [rabbit]) end, sets:new(), Apps)), - io:format("Target Apps = ~p~n", [TargetApps]), try ok = app_utils:stop_applications( TargetApps, handle_app_error(error_during_shutdown)) after try - io:format("Running Cleanup~n"), BootSteps = load_steps(rabbit_boot_step), ToDelete = [Step || {App, _, _}=Step <- BootSteps, lists:member(App, TargetApps)], - io:format("Boot steps on shutdown: ~p~n", [ToDelete]), - [begin - ets:delete(?MODULE, Step), - io:format("Deleted ~p~n", [Step]) - end || {_, Step, _} <- ToDelete], - io:format("Run cleanup steps~n"), + [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete], run_cleanup_steps(TargetApps) after - io:format("save boot table~n"), save_boot_table() end, [begin {ok, Mods} = application:get_key(App, modules), - io:format("cleanup ~p...~n", [Mods]), [begin code:soft_purge(Mod), code:delete(Mod), diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index b7222c1e..47ece039 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -283,9 +283,17 @@ write_enabled_plugins(PluginsFile, Plugins) -> action_change(Node, Action, Targets) -> rpc_call(Node, rabbit_plugins, Action, [Targets]). -rpc_call(Node, Mod, Fun, Args) -> - case rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT) of +rpc_call(Node, Mod, Action, Args) -> + case rpc:call(Node, Mod, Action, Args, ?RPC_TIMEOUT) of {badrpc, nodedown} -> io:format("Plugin configuration has changed.~n"); - _ -> ok + ok -> io:format("Plugin(s) ~pd.~n", [Action]); + %% QA question: if we get into a situation where the rpc call fails, + %% does it make sense to suggest a restart as we do here? The restart + %% would only succeed if the failure (here) was due to a bug in the + %% rabbit_plugins:enable/1 code afaict. + Error -> io:format("Unable to ~p plugin(s). " + "Please restart the broker " + "to apply your changes.~nError: ~p~n", + [Action, Error]) end. -- cgit v1.2.1 From 49815f8e33e3b7a3875b8d73be54652ecbc070a6 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 26 Nov 2013 12:17:28 +0000 Subject: De-duplicate dependency/graph building functions --- src/app_utils.erl | 31 +++++++++++++------------------ src/rabbit_misc.erl | 35 +++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 22e2df44..82e4e276 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -78,32 +78,27 @@ wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. direct_dependencies(Root) -> - G = digraph:new(), Loaded = application:loaded_applications(), - try - [begin - case digraph:vertex(G, App) of - false -> digraph:add_vertex(G, App, App); - _ -> ok = throw({graph_error, {vertex, duplicate, App}}) - end - end || {App, _, _} <- Loaded], - [digraph:add_edge(G, App, Dep) || - {App, Deps} <- [{App, app_dependencies(App)} || + {ok, G} = rabbit_misc:build_graph( + fun() -> [{App, App} || {App, _, _} <- Loaded] end, + fun() -> [{App, Dep} || + {App, Deps} <- [{App, app_dependencies(App)} || {App, _Desc, _Vsn} <- Loaded], - Dep <- Deps], + Dep <- Deps] + end, + digraph:new()), + try Deps = lists:foldl( fun(E, Acc) -> - {_, _InVertex, OutVertex, _Label} = digraph:edge(G, E), - case is_reachable(G, OutVertex, Root) of - [] -> sets:add_element(OutVertex, Acc); - _ -> Acc - end + {_, _InVertex, OutVertex, _Label} = digraph:edge(G, E), + case is_reachable(G, OutVertex, Root) of + [] -> sets:add_element(OutVertex, Acc); + _ -> Acc + end end, sets:from_list([Root]), digraph:out_edges(G, Root)), sets:to_list(Deps) - catch {graph_error, Reason} -> - {error, Reason} after true = digraph:delete(G) end. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 26071a8a..3708c625 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -52,7 +52,7 @@ -export([gb_trees_fold/3, gb_trees_foreach/2]). -export([parse_arguments/3]). -export([all_app_module_attributes/1]). --export([all_module_attributes/1, build_acyclic_graph/3]). +-export([all_module_attributes/1, build_graph/3, build_acyclic_graph/3]). -export([now_ms/0]). -export([const_ok/0, const/1]). -export([ntoa/1, ntoab/1]). @@ -91,8 +91,12 @@ :: rabbit_types:channel_exit() | rabbit_types:connection_exit()). -type(digraph_label() :: term()). -type(graph_vertex_fun() :: + fun (() -> [{digraph:vertex(), digraph_label()}])). +-type(acyclic_graph_vertex_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph_label()}])). -type(graph_edge_fun() :: + fun (() -> [{digraph:vertex(), digraph:vertex()}])). +-type(acyclic_graph_edge_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph:vertex()}])). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) @@ -210,8 +214,17 @@ -> {'ok', {atom(), [{string(), string()}], [string()]}} | 'no_command'). -spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]). +-spec(build_graph/3 :: + (graph_vertex_fun(), graph_edge_fun(), + [{atom(), [term()]}]) + -> rabbit_types:ok_or_error2(digraph(), + {'vertex', 'duplicate', digraph:vertex()} | + {'edge', ({bad_vertex, digraph:vertex()} | + {bad_edge, [digraph:vertex()]}), + digraph:vertex(), digraph:vertex()})). -spec(build_acyclic_graph/3 :: - (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) + (acyclic_graph_vertex_fun(), + acyclic_graph_edge_fun(), [{atom(), [term()]}]) -> rabbit_types:ok_or_error2(digraph(), {'vertex', 'duplicate', digraph:vertex()} | {'edge', ({bad_vertex, digraph:vertex()} | @@ -884,25 +897,31 @@ find_module_attributes(Generator, Fold) -> {ok, Modules} <- [application:get_key(App, modules)]])), lists:foldl(Fold, [], Targets). -build_acyclic_graph(VertexFun, EdgeFun, Graph) -> - G = digraph:new([acyclic]), +build_graph(VertexFun, EdgeFun, G) -> try [case digraph:vertex(G, Vertex) of false -> digraph:add_vertex(G, Vertex, Label); _ -> ok = throw({graph_error, {vertex, duplicate, Vertex}}) - end || {Module, Atts} <- Graph, - {Vertex, Label} <- VertexFun(Module, Atts)], + end || {Vertex, Label} <- VertexFun()], [case digraph:add_edge(G, From, To) of {error, E} -> throw({graph_error, {edge, E, From, To}}); _ -> ok - end || {Module, Atts} <- Graph, - {From, To} <- EdgeFun(Module, Atts)], + end || {From, To} <- EdgeFun()], {ok, G} catch {graph_error, Reason} -> true = digraph:delete(G), {error, Reason} end. +build_acyclic_graph(VertexFun, EdgeFun, Graph) -> + build_graph(fun() -> [Vertex || {Module, Atts} <- Graph, + Vertex <- VertexFun(Module, Atts)] + end, + fun() -> [Edge || {Module, Atts} <- Graph, + Edge <- EdgeFun(Module, Atts)] + end, + digraph:new([acyclic])). + const_ok() -> ok. const(X) -> fun () -> X end. -- cgit v1.2.1 From b9454e93a8396ee4324c9035bf9c6cae4a95a782 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 26 Nov 2013 12:56:19 +0000 Subject: refactor (renaming) --- src/app_utils.erl | 6 +++--- src/rabbit_boot.erl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 82e4e276..1037fa34 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -18,7 +18,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, running_applications/0, wait_for_applications/1, app_dependencies/1]). --export([direct_dependencies/1]). +-export([isolated_dependencies/1]). -ifdef(use_specs). @@ -32,7 +32,7 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. --spec direct_dependencies(atom()) -> [atom()]. +-spec isolated_dependencies(atom()) -> [atom()]. -endif. @@ -77,7 +77,7 @@ stop_applications(Apps, ErrorHandler) -> wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. -direct_dependencies(Root) -> +isolated_dependencies(Root) -> Loaded = application:loaded_applications(), {ok, G} = rabbit_misc:build_graph( fun() -> [{App, App} || {App, _, _} <- Loaded] end, diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index d1b32b6a..3f0ceea6 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -118,7 +118,7 @@ stop(Apps) -> lists:foldl( fun(App, Set) -> lists:foldl(fun sets:add_element/2, Set, - app_utils:direct_dependencies(App) -- [rabbit]) + app_utils:isolated_dependencies(App) -- [rabbit]) end, sets:new(), Apps)), try ok = app_utils:stop_applications( -- cgit v1.2.1 From 4ca67b9411309883b3a87802f2530dbd44f296aa Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 16 Dec 2013 16:06:12 +0000 Subject: Tidy/Refactor boot step handling Simplify the boot step loading/application code and switch from using a discrete rabbit_cleanup_step module attribute; We now search the regular boot steps directly for ?cleanup? entries. --- src/rabbit_boot.erl | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 3f0ceea6..29abaefe 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -125,7 +125,7 @@ stop(Apps) -> TargetApps, handle_app_error(error_during_shutdown)) after try - BootSteps = load_steps(rabbit_boot_step), + BootSteps = load_steps(), ToDelete = [Step || {App, _, _}=Step <- BootSteps, lists:member(App, TargetApps)], [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete], @@ -150,22 +150,21 @@ run_cleanup_steps(Apps) -> fun({_, Name, _}=Step, Acc) -> case sets:is_element(Name, Completed) of true -> Acc; - false -> run_boot_step(Step, rabbit_cleanup_step), + false -> run_cleanup_step(Step), sets:add_element(Name, Acc) end end, Completed, - [Step || {App, _, _}=Step <- load_steps(rabbit_cleanup_step), - lists:member(App, Apps)]), + [Step || {App, _, _}=Step <- load_steps(), lists:member(App, Apps)]), ok. run_boot_steps() -> - Steps = load_steps(rabbit_boot_step), - [ok = run_boot_step(Step, rabbit_boot_step) || Step <- Steps], + Steps = load_steps(), + [ok = run_boot_step(Step) || Step <- Steps], ok. -load_steps(StepType) -> - StepAttrs = rabbit_misc:all_app_module_attributes(StepType), +load_steps() -> + StepAttrs = rabbit_misc:all_app_module_attributes(rabbit_boot_step), sort_boot_steps( lists:usort( [{Mod, {AppName, Steps}} || {AppName, Mod, Steps} <- StepAttrs])). @@ -280,18 +279,20 @@ handle_app_error(Term) -> throw({Term, App, Reason}) end. -run_boot_step({_, _, Attributes}, rabbit_cleanup_step) -> - run_step(Attributes); -run_boot_step({_, StepName, Attributes}, rabbit_boot_step) -> +run_cleanup_step({_, _, Attributes}) -> + run_step_name(Attributes, cleanup). + +run_boot_step({_, StepName, Attributes}) -> case catch already_run(StepName) of - false -> ok = run_step(Attributes), + false -> ok = run_step_name(Attributes, mfa), mark_complete(StepName); true -> ok end, ok. -run_step(Attributes) -> - case [MFA || {mfa, MFA} <- Attributes] of +run_step_name(Attributes, AttributeName) -> + case [MFA || {Key, MFA} <- Attributes, + Key =:= AttributeName] of [] -> ok; MFAs -> -- cgit v1.2.1 From f1d90740445923d4e8db8878630bac9784586e02 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 10 Jan 2014 23:48:46 +0000 Subject: Handle hard vs. soft boot step dependencies --- src/rabbit_boot.erl | 52 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index c7b4ad94..7112af7c 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -90,7 +90,7 @@ boot_with(StartFun) -> shutdown(Apps) -> try - case whereis(rabbit_boot) of + case whereis(?MODULE) of undefined -> ok; _ -> await_startup(Apps) end, @@ -130,7 +130,7 @@ stop(Apps) -> TargetApps, handle_app_error(error_during_shutdown)) after try - BootSteps = load_steps(), + BootSteps = load_steps(boot), ToDelete = [Step || {App, _, _}=Step <- BootSteps, lists:member(App, TargetApps)], [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete], @@ -160,17 +160,20 @@ run_cleanup_steps(Apps) -> end end, Completed, - [Step || {App, _, _}=Step <- load_steps(), lists:member(App, Apps)]), + [Step || {App, _, _}=Step <- load_steps(cleanup), + lists:member(App, Apps)]), ok. run_boot_steps() -> - Steps = load_steps(), + Steps = load_steps(boot), [ok = run_boot_step(Step) || Step <- Steps], ok. -load_steps() -> +load_steps(Type) -> + io:format("Loading steps for ~p~n", [Type]), StepAttrs = rabbit_misc:all_app_module_attributes(rabbit_boot_step), sort_boot_steps( + Type, lists:usort( [{Mod, {AppName, Steps}} || {AppName, Mod, Steps} <- StepAttrs])). @@ -341,16 +344,35 @@ log_location(Type) -> vertices(_Module, {AppName, Steps}) -> [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps]. -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]. +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 {Key, {hard, StepName}}) + %% as needing to run before or after our own cleanup actions. + 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. -sort_boot_steps(UnsortedSteps) -> - case rabbit_misc:build_acyclic_graph(fun vertices/2, fun edges/2, +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 @@ -365,7 +387,7 @@ sort_boot_steps(UnsortedSteps) -> %% Check that all mentioned {M,F,A} triples are exported. case [{StepName, {M,F,A}} || {_App, StepName, Attributes} <- SortedSteps, - {mfa, {M,F,A}} <- Attributes, + {mfa, {M,F,A}} <- Attributes, not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; MissingFunctions -> basic_boot_error( -- cgit v1.2.1 From b3bcf2bb1bd78d2ca1e417b69e915332c13945d7 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Sun, 12 Jan 2014 12:38:38 +0000 Subject: Refactor (simplify application stop handling) The offline plugin handling does a fine job of detecting which apps need to be stopped/unloaded when disabling a plugin at runtime. We previously had problems accidentally stopping things like stdlib and mnesia by accident, but only because we went searching for the app dependencies ourselves! Instead, we stop only those applications (i.e., plugins) that rabbit_plugins_main has requested. In doing so, we remove a considerable amount of (new) digraph hacking from app_utils and reduce the complexity of rabbit_boot somewhat. --- src/app_utils.erl | 36 ------------------------------------ src/rabbit_boot.erl | 21 +++++++-------------- 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 1037fa34..4622f6e0 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -18,7 +18,6 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, running_applications/0, wait_for_applications/1, app_dependencies/1]). --export([isolated_dependencies/1]). -ifdef(use_specs). @@ -32,7 +31,6 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. --spec isolated_dependencies(atom()) -> [atom()]. -endif. @@ -77,35 +75,6 @@ stop_applications(Apps, ErrorHandler) -> wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. -isolated_dependencies(Root) -> - Loaded = application:loaded_applications(), - {ok, G} = rabbit_misc:build_graph( - fun() -> [{App, App} || {App, _, _} <- Loaded] end, - fun() -> [{App, Dep} || - {App, Deps} <- [{App, app_dependencies(App)} || - {App, _Desc, _Vsn} <- Loaded], - Dep <- Deps] - end, - digraph:new()), - try - Deps = lists:foldl( - fun(E, Acc) -> - {_, _InVertex, OutVertex, _Label} = digraph:edge(G, E), - case is_reachable(G, OutVertex, Root) of - [] -> sets:add_element(OutVertex, Acc); - _ -> Acc - end - end, - sets:from_list([Root]), - digraph:out_edges(G, Root)), - sets:to_list(Deps) - after - true = digraph:delete(G) - end. - -is_reachable(G, OutVertex, Root) -> - digraph_utils:reaching_neighbours([OutVertex], G) -- [Root]. - app_dependency_order(RootApps, StripUnreachable) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, @@ -127,11 +96,6 @@ app_dependency_order(RootApps, StripUnreachable) -> %%--------------------------------------------------------------------------- %% Private API -%% It might be worth documenting this on the plugin author's guide/page. -%% A plugin should expect its boot steps to run /before/ the application -%% is started, and its cleanup steps to run /after/ the application has -%% fully stopped. - wait_for_application(Application) -> case lists:keymember(Application, 1, rabbit_misc:which_applications()) of true -> ok; diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 7112af7c..12a01161 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -118,23 +118,16 @@ start(Apps) -> stop(Apps) -> ensure_boot_table(), - TargetApps = - sets:to_list( - lists:foldl( - fun(App, Set) -> - lists:foldl(fun sets:add_element/2, Set, - app_utils:isolated_dependencies(App) -- [rabbit]) - end, sets:new(), Apps)), try ok = app_utils:stop_applications( - TargetApps, handle_app_error(error_during_shutdown)) + Apps, handle_app_error(error_during_shutdown)) after try BootSteps = load_steps(boot), ToDelete = [Step || {App, _, _}=Step <- BootSteps, - lists:member(App, TargetApps)], + lists:member(App, Apps)], [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete], - run_cleanup_steps(TargetApps) + run_cleanup_steps(Apps) after save_boot_table() end, @@ -146,7 +139,7 @@ stop(Apps) -> false = code:is_loaded(Mod) end || Mod <- Mods], application:unload(App) - end || App <- TargetApps] + end || App <- Apps] end. run_cleanup_steps(Apps) -> @@ -170,7 +163,6 @@ run_boot_steps() -> ok. load_steps(Type) -> - io:format("Loading steps for ~p~n", [Type]), StepAttrs = rabbit_misc:all_app_module_attributes(rabbit_boot_step), sort_boot_steps( Type, @@ -347,8 +339,9 @@ vertices(_Module, {AppName, 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 {Key, {hard, StepName}}) - %% as needing to run before or after our own cleanup actions. + %% consider /hard/ dependencies (i.e., of the form + %% {DependencyType, {hard, StepName}}) as needing to run before or after + %% our own cleanup actions. fun (_Module, {_AppName, Steps}) -> [case Key of requires -> {StepName, strip_type(OtherStep)}; -- cgit v1.2.1 From 8f6cd45060f3604b78d36799aac27574850ae516 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Sun, 12 Jan 2014 15:10:59 +0000 Subject: Refactor - remove unused functions/comments/todos --- src/app_utils.erl | 5 +---- src/rabbit_boot.erl | 2 +- src/rabbit_misc.erl | 41 ++++++++++++----------------------------- src/rabbit_plugins.erl | 2 -- src/rabbit_plugins_main.erl | 4 ---- 5 files changed, 14 insertions(+), 40 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 4622f6e0..5eeb6924 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -17,7 +17,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, - running_applications/0, wait_for_applications/1, app_dependencies/1]). + wait_for_applications/1, app_dependencies/1]). -ifdef(use_specs). @@ -37,9 +37,6 @@ %%--------------------------------------------------------------------------- %% Public API -running_applications() -> - [App || {App, _, _} <- application:which_applications()]. - load_applications(Apps) -> load_applications(queue:from_list(Apps), sets:new()), ok. diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 12a01161..9ccad903 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -163,7 +163,7 @@ run_boot_steps() -> ok. load_steps(Type) -> - StepAttrs = rabbit_misc:all_app_module_attributes(rabbit_boot_step), + StepAttrs = rabbit_misc:all_module_attributes_with_app(rabbit_boot_step), sort_boot_steps( Type, lists:usort( diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 59d6401f..4bfd0da3 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -51,8 +51,8 @@ -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). -export([gb_trees_fold/3, gb_trees_foreach/2]). -export([parse_arguments/3]). --export([all_app_module_attributes/1]). --export([all_module_attributes/1, build_graph/3, build_acyclic_graph/3]). +-export([all_module_attributes_with_app/1]). +-export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). -export([const_ok/0, const/1]). -export([ntoa/1, ntoab/1]). @@ -92,12 +92,8 @@ :: rabbit_types:channel_exit() | rabbit_types:connection_exit()). -type(digraph_label() :: term()). -type(graph_vertex_fun() :: - fun (() -> [{digraph:vertex(), digraph_label()}])). --type(acyclic_graph_vertex_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph_label()}])). -type(graph_edge_fun() :: - fun (() -> [{digraph:vertex(), digraph:vertex()}])). --type(acyclic_graph_edge_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph:vertex()}])). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) @@ -215,17 +211,10 @@ -> {'ok', {atom(), [{string(), string()}], [string()]}} | 'no_command'). -spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]). --spec(build_graph/3 :: - (graph_vertex_fun(), graph_edge_fun(), - [{atom(), [term()]}]) - -> rabbit_types:ok_or_error2(digraph(), - {'vertex', 'duplicate', digraph:vertex()} | - {'edge', ({bad_vertex, digraph:vertex()} | - {bad_edge, [digraph:vertex()]}), - digraph:vertex(), digraph:vertex()})). +-spec(all_module_attributes_with_app/1 :: + (atom()) -> [{atom(), atom(), [term()]}]). -spec(build_acyclic_graph/3 :: - (acyclic_graph_vertex_fun(), - acyclic_graph_edge_fun(), [{atom(), [term()]}]) + (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) -> rabbit_types:ok_or_error2(digraph(), {'vertex', 'duplicate', digraph:vertex()} | {'edge', ({bad_vertex, digraph:vertex()} | @@ -867,7 +856,7 @@ module_attributes(Module) -> V end. -all_app_module_attributes(Name) -> +all_module_attributes_with_app(Name) -> find_module_attributes( fun(App, Modules) -> [{App, Module} || Module <- Modules] @@ -900,31 +889,25 @@ find_module_attributes(Generator, Fold) -> {ok, Modules} <- [application:get_key(App, modules)]])), lists:foldl(Fold, [], Targets). -build_graph(VertexFun, EdgeFun, G) -> +build_acyclic_graph(VertexFun, EdgeFun, Graph) -> + G = digraph:new([acyclic]), try [case digraph:vertex(G, Vertex) of false -> digraph:add_vertex(G, Vertex, Label); _ -> ok = throw({graph_error, {vertex, duplicate, Vertex}}) - end || {Vertex, Label} <- VertexFun()], + end || {Module, Atts} <- Graph, + {Vertex, Label} <- VertexFun(Module, Atts)], [case digraph:add_edge(G, From, To) of {error, E} -> throw({graph_error, {edge, E, From, To}}); _ -> ok - end || {From, To} <- EdgeFun()], + end || {Module, Atts} <- Graph, + {From, To} <- EdgeFun(Module, Atts)], {ok, G} catch {graph_error, Reason} -> true = digraph:delete(G), {error, Reason} end. -build_acyclic_graph(VertexFun, EdgeFun, Graph) -> - build_graph(fun() -> [Vertex || {Module, Atts} <- Graph, - Vertex <- VertexFun(Module, Atts)] - end, - fun() -> [Edge || {Module, Atts} <- Graph, - Edge <- EdgeFun(Module, Atts)] - end, - digraph:new([acyclic])). - const_ok() -> ok. const(X) -> fun () -> X end. diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 81cebd99..6fe4c127 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -38,8 +38,6 @@ %%---------------------------------------------------------------------------- -%% TODO: move all the {enable,disable}ment logic from plugins_main to here! - enable(Plugins) -> setup(), rabbit_boot:start(Plugins). diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 47ece039..408fc4e1 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -287,10 +287,6 @@ rpc_call(Node, Mod, Action, Args) -> case rpc:call(Node, Mod, Action, Args, ?RPC_TIMEOUT) of {badrpc, nodedown} -> io:format("Plugin configuration has changed.~n"); ok -> io:format("Plugin(s) ~pd.~n", [Action]); - %% QA question: if we get into a situation where the rpc call fails, - %% does it make sense to suggest a restart as we do here? The restart - %% would only succeed if the failure (here) was due to a bug in the - %% rabbit_plugins:enable/1 code afaict. Error -> io:format("Unable to ~p plugin(s). " "Please restart the broker " "to apply your changes.~nError: ~p~n", -- cgit v1.2.1 From e4d6e5d398c9edb24130e59926a2079923aa2988 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Feb 2014 13:54:28 +0000 Subject: Cosmetic (ish) --- src/rabbit_boot.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 9ccad903..8dc9bff5 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -340,8 +340,7 @@ 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 needing to run before or after - %% our own cleanup actions. + %% {DependencyType, {hard, StepName}}) as dependencies. fun (_Module, {_AppName, Steps}) -> [case Key of requires -> {StepName, strip_type(OtherStep)}; @@ -406,5 +405,3 @@ sort_boot_steps(Type, UnsortedSteps) -> io_lib:format(" depends on ~w~n", [First])] end]) end. - - -- cgit v1.2.1 From 6592206a6d35ad7c5f79af0efaa24178fc7266c3 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Feb 2014 13:57:20 +0000 Subject: If you're going to catch, actually handle failures --- src/rabbit_boot.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 8dc9bff5..d2dca2a4 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -286,7 +286,7 @@ run_boot_step({_, StepName, Attributes}) -> case catch already_run(StepName) of false -> ok = run_step_name(Attributes, mfa), mark_complete(StepName); - true -> ok + _ -> ok end, ok. -- cgit v1.2.1 From 3b5025ef7b4d4b129023da2d86dc91bf25160c06 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Feb 2014 13:58:23 +0000 Subject: Simplify --- src/rabbit_boot.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index d2dca2a4..489410fa 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -220,9 +220,8 @@ reload_all(Modules) -> load_mod(Mod) -> case code:is_loaded(Mod) of - {file, preloaded} -> code:load_file(Mod); - {file, Path} -> code:load_abs(Path); - false -> code:load_file(Mod) + {file, Path} when Path /= 'preloaded' -> code:load_abs(Path); + _ -> code:load_file(Mod) end. await_startup(Apps) -> -- cgit v1.2.1 From 6f2614c0c78b4011f17924c0b146fe7b3c7f6299 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Feb 2014 13:59:07 +0000 Subject: Don't bother printing out reload errors --- src/rabbit_boot.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 489410fa..8561e7a5 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -214,7 +214,7 @@ reload_all(Modules) -> [begin case code:soft_purge(Mod) of true -> load_mod(Mod); - false -> io:format("unable to purge ~p~n", [Mod]) + false -> ok end end || Mod <- Modules]. -- cgit v1.2.1 From b1da2cb791ae71535db9fb18092fbcbcc8bdcca8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Feb 2014 16:06:16 +0000 Subject: Avoid verbosity --- src/rabbit_boot.erl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 8561e7a5..1f8fcf1d 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -40,14 +40,13 @@ %% 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 and run them in dependent -%% order. +%% the boot steps from all loaded applications. %% -%% When the broker is already running, we must run all the boot steps for -%% each application/plugin we're starting, plus any other (dependent) steps -%% that have not been run. To achieve this, we process the boot steps from -%% all loaded applications, but skip those that have already been run (i.e., -%% steps that have been run once whilst or since the broker started). +%% 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 the boot steps from all loaded applications, +%% but skip those that have already been run (i.e., steps that have been run +%% once whilst, or even since the broker started). %% %% Tracking which boot steps have already been run is done via an ets table. %% Because we frequently find ourselves needing to query this table without @@ -58,8 +57,6 @@ %% starting, the file is also removed and replaced (since we do not want to %% track boot steps from a previous incarnation of the node). %% -%% Cleanup steps... -%% %%--------------------------------------------------------------------------- %% Public API -- cgit v1.2.1 From 37a44282681ceadfe812810a1ff1b25f0dc9fbba Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 18 Feb 2014 13:03:29 +0000 Subject: Use a gen_server to keep the boot steps table around --- src/rabbit.erl | 6 ++ src/rabbit_boot.erl | 158 +++++++++++++++++++++++++--------------------------- 2 files changed, 83 insertions(+), 81 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 081e2e22..102d9815 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -41,6 +41,12 @@ {requires, pre_boot}, {enables, external_infrastructure}]}). +-rabbit_boot_step({boot_table, + [{mfa, {rabbit_sup, start_child, + [rabbit_boot_table, rabbit_boot, []]}}, + {requires, file_handle_cache}, + {enables, external_infrastructure}]}). + -rabbit_boot_step({database, [{mfa, {rabbit_mnesia, init, []}}, {requires, file_handle_cache}, diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 1f8fcf1d..1af1725d 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -17,10 +17,13 @@ -module(rabbit_boot). -export([boot_with/1, shutdown/1]). --export([start/1, stop/1]). +-export([start_link/0, start/1, stop/1]). -export([run_boot_steps/0]). -export([boot_error/2, boot_error/4]). +-export([init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3]). + -ifdef(use_specs). -spec(boot_with/1 :: (fun(() -> 'ok')) -> 'ok'). @@ -36,6 +39,9 @@ -define(BOOT_FILE, "boot.info"). +-define(INIT_SERVER, rabbit_boot_table_initial). +-define(SERVER, rabbit_boot_table). + %% %% 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 @@ -58,11 +64,48 @@ %% track boot steps from a previous incarnation of the node). %% +%%--------------------------------------------------------------------------- +%% gen_server API - we use a gen_server to keep our ets table around for the +%% lifetime of the broker. Since we can't add a gen_server to rabbit_sup +%% during the initial boot phase (because boot-steps need evaluating prior to +%% starting rabbit_sup), we overload the gen_server init/2 callback, providing +%% an initial table owner and subsequent heir that gets hooked into rabbit_sup +%% once it has started. + +start_link() -> gen_server:start_link(?MODULE, [?SERVER], []). + +init([?INIT_SERVER]) -> + ets:new(?MODULE, [named_table, public, ordered_set]), + {ok, ?INIT_SERVER}; +init(_) -> + %% if we crash (?), we'll need to re-create our ets table when rabbit_sup + %% restarts us, so there's no guarantee that the INIT_SERVER will be + %% alive when we get here! + case catch gen_server:call(?INIT_SERVER, {inheritor, self()}) of + {'EXIT', {noproc, _}} -> init([?INIT_SERVER]); + ok -> ok + end, + {ok, ?SERVER}. + +handle_call({inheritor, Pid}, From, ?INIT_SERVER) -> + %% setting the hier seems simpler than using give_away here + ets:setopts(?MODULE, [{heir, Pid, []}]), + gen_server:reply(From, ok), + {stop, normal, ?INIT_SERVER}; +handle_call(_, _From, ?SERVER) -> + {noreply, ?SERVER}. + +handle_cast(_, State) -> {noreply, State}. +handle_info(_, State) -> {noreply, State}. +terminate(_, _) -> ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + %%--------------------------------------------------------------------------- %% Public API boot_with(StartFun) -> - %% TODO: this should be done with monitors, not links, I think Marker = spawn_link(fun() -> receive stop -> ok end end), case catch register(rabbit_boot, Marker) of true -> try @@ -86,32 +129,24 @@ boot_with(StartFun) -> end. shutdown(Apps) -> - try - case whereis(?MODULE) of - undefined -> ok; - _ -> await_startup(Apps) - end, - %% TODO: put this back in somewhere sensible... - %% rabbit_log:info("Stopping RabbitMQ~n"), - ok = app_utils:stop_applications(Apps) - after - delete_boot_table() - end. + case whereis(?MODULE) of + undefined -> ok; + _ -> await_startup(Apps) + end, + %% TODO: put this back in somewhere sensible... + %% rabbit_log:info("Stopping RabbitMQ~n"), + ok = app_utils:stop_applications(Apps). start(Apps) -> - try - ensure_boot_table(), - force_reload(Apps), - StartupApps = app_utils:app_dependency_order(Apps, false), - case whereis(?MODULE) of - undefined -> run_boot_steps(); - _ -> ok - end, - ok = app_utils:start_applications(StartupApps, - handle_app_error(could_not_start)) - after - save_boot_table() - end. + ensure_boot_table(), + force_reload(Apps), + StartupApps = app_utils:app_dependency_order(Apps, false), + case whereis(?MODULE) of + undefined -> run_boot_steps(); + _ -> ok + end, + ok = app_utils:start_applications(StartupApps, + handle_app_error(could_not_start)). stop(Apps) -> ensure_boot_table(), @@ -119,15 +154,11 @@ stop(Apps) -> ok = app_utils:stop_applications( Apps, handle_app_error(error_during_shutdown)) after - try - BootSteps = load_steps(boot), - ToDelete = [Step || {App, _, _}=Step <- BootSteps, - lists:member(App, Apps)], - [ets:delete(?MODULE, Step) || {_, Step, _} <- ToDelete], - run_cleanup_steps(Apps) - after - save_boot_table() - end, + BootSteps = 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 @@ -224,50 +255,13 @@ load_mod(Mod) -> await_startup(Apps) -> app_utils:wait_for_applications(Apps). -delete_boot_table() -> - case filelib:is_file(boot_file()) of - true -> file:delete(boot_file()); - false -> ok - end. - ensure_boot_table() -> - case whereis(?MODULE) of - undefined -> - case filelib:is_file(boot_file()) of - true -> load_table(); - false -> clean_table() - end; - _Pid -> - clean_table() - end. - -clean_table() -> - delete_boot_table(), case ets:info(?MODULE) of - undefined -> - ets:new(?MODULE, [named_table, public, ordered_set]); - _ -> - ok + undefined -> gen_server:start({local, ?INIT_SERVER}, ?MODULE, + [?INIT_SERVER], []); + _Pid -> ok end. -load_table() -> - case ets:file2tab(boot_file(), [{verify, true}]) of - {error, _} -> clean_table(); - {ok, _Tab} -> ok - end. - -save_boot_table() -> - delete_boot_table(), - case ets:info(?MODULE) of - undefined -> ok; - _ -> ets:tab2file(?MODULE, boot_file(), - [{extended_info, [object_count]}]), - ets:delete(?MODULE) - end. - -boot_file() -> - filename:join(rabbit_mnesia:dir(), ?BOOT_FILE). - handle_app_error(Term) -> fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> throw({Term, App, ExitReason}); @@ -275,18 +269,18 @@ handle_app_error(Term) -> throw({Term, App, Reason}) end. -run_cleanup_step({_, _, Attributes}) -> - run_step_name(Attributes, cleanup). +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(Attributes, mfa), + false -> ok = run_step_name(StepName, Attributes, mfa), mark_complete(StepName); _ -> ok end, ok. -run_step_name(Attributes, AttributeName) -> +run_step_name(StepName, Attributes, AttributeName) -> case [MFA || {Key, MFA} <- Attributes, Key =:= AttributeName] of [] -> @@ -296,9 +290,11 @@ run_step_name(Attributes, AttributeName) -> apply(M,F,A) of ok -> ok; - {error, Reason} -> boot_error(Reason, not_available) + {error, Reason} -> boot_error({boot_step, StepName, Reason}, + not_available) catch - _:Reason -> boot_error(Reason, erlang:get_stacktrace()) + _:Reason -> boot_error({boot_step, StepName, Reason}, + erlang:get_stacktrace()) end || {M,F,A} <- MFAs], ok end. -- cgit v1.2.1 From e280ff6cc2bebfb13e3f6ca8951bf931e6e9fade Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 18 Feb 2014 13:55:51 +0000 Subject: Simplifying refactor Since the "rabbit" process /always/ starts before everything else, handle boot table creation at that point and avoid the ets ownership issue and corresponding messiness. The previous worries about handling situations where we want to run rabbit_boot code whilst rabbit is offline aren't an issue for us. --- src/rabbit.erl | 7 +---- src/rabbit_boot.erl | 80 +++++++---------------------------------------------- 2 files changed, 11 insertions(+), 76 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 102d9815..3690329f 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -41,12 +41,6 @@ {requires, pre_boot}, {enables, external_infrastructure}]}). --rabbit_boot_step({boot_table, - [{mfa, {rabbit_sup, start_child, - [rabbit_boot_table, rabbit_boot, []]}}, - {requires, file_handle_cache}, - {enables, external_infrastructure}]}). - -rabbit_boot_step({database, [{mfa, {rabbit_mnesia, init, []}}, {requires, file_handle_cache}, @@ -425,6 +419,7 @@ start(normal, []) -> true = register(rabbit, self()), print_banner(), log_banner(), + rabbit_boot:prepare_boot_table(), rabbit_boot:run_boot_steps(), {ok, SupPid}; Error -> diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 1af1725d..018a3899 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -16,14 +16,12 @@ -module(rabbit_boot). +-export([prepare_boot_table/0]). -export([boot_with/1, shutdown/1]). --export([start_link/0, start/1, stop/1]). +-export([start/1, stop/1]). -export([run_boot_steps/0]). -export([boot_error/2, boot_error/4]). --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3]). - -ifdef(use_specs). -spec(boot_with/1 :: (fun(() -> 'ok')) -> 'ok'). @@ -37,12 +35,6 @@ -endif. --define(BOOT_FILE, "boot.info"). - --define(INIT_SERVER, rabbit_boot_table_initial). --define(SERVER, rabbit_boot_table). - -%% %% 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 @@ -50,61 +42,18 @@ %% %% 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 the boot steps from all loaded applications, -%% but skip those that have already been run (i.e., steps that have been run -%% once whilst, or even since the broker started). -%% -%% Tracking which boot steps have already been run is done via an ets table. -%% Because we frequently find ourselves needing to query this table without -%% the rabbit application running (e.g., during the initial boot sequence -%% and when we've "cold started" a node without any running applications), -%% this table is serialized to a file after each operation. When the node is -%% stopped cleanly, the file is deleted. When a node is in the process of -%% starting, the file is also removed and replaced (since we do not want to -%% track boot steps from a previous incarnation of the node). -%% - -%%--------------------------------------------------------------------------- -%% gen_server API - we use a gen_server to keep our ets table around for the -%% lifetime of the broker. Since we can't add a gen_server to rabbit_sup -%% during the initial boot phase (because boot-steps need evaluating prior to -%% starting rabbit_sup), we overload the gen_server init/2 callback, providing -%% an initial table owner and subsequent heir that gets hooked into rabbit_sup -%% once it has started. - -start_link() -> gen_server:start_link(?MODULE, [?SERVER], []). - -init([?INIT_SERVER]) -> - ets:new(?MODULE, [named_table, public, ordered_set]), - {ok, ?INIT_SERVER}; -init(_) -> - %% if we crash (?), we'll need to re-create our ets table when rabbit_sup - %% restarts us, so there's no guarantee that the INIT_SERVER will be - %% alive when we get here! - case catch gen_server:call(?INIT_SERVER, {inheritor, self()}) of - {'EXIT', {noproc, _}} -> init([?INIT_SERVER]); - ok -> ok - end, - {ok, ?SERVER}. - -handle_call({inheritor, Pid}, From, ?INIT_SERVER) -> - %% setting the hier seems simpler than using give_away here - ets:setopts(?MODULE, [{heir, Pid, []}]), - gen_server:reply(From, ok), - {stop, normal, ?INIT_SERVER}; -handle_call(_, _From, ?SERVER) -> - {noreply, ?SERVER}. - -handle_cast(_, State) -> {noreply, State}. -handle_info(_, State) -> {noreply, State}. -terminate(_, _) -> ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. +%% 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]). + boot_with(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), case catch register(rabbit_boot, Marker) of @@ -138,7 +87,6 @@ shutdown(Apps) -> ok = app_utils:stop_applications(Apps). start(Apps) -> - ensure_boot_table(), force_reload(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), case whereis(?MODULE) of @@ -149,7 +97,6 @@ start(Apps) -> handle_app_error(could_not_start)). stop(Apps) -> - ensure_boot_table(), try ok = app_utils:stop_applications( Apps, handle_app_error(error_during_shutdown)) @@ -255,13 +202,6 @@ load_mod(Mod) -> await_startup(Apps) -> app_utils:wait_for_applications(Apps). -ensure_boot_table() -> - case ets:info(?MODULE) of - undefined -> gen_server:start({local, ?INIT_SERVER}, ?MODULE, - [?INIT_SERVER], []); - _Pid -> ok - end. - handle_app_error(Term) -> fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> throw({Term, App, ExitReason}); -- cgit v1.2.1 From d5385319bcc1866efca37e1e66dbda1a990c64a4 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 18 Feb 2014 14:05:05 +0000 Subject: Add a type spec for prepare_boot_table --- src/rabbit_boot.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 018a3899..d0986442 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -24,14 +24,15 @@ -ifdef(use_specs). --spec(boot_with/1 :: (fun(() -> 'ok')) -> '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()). +-spec(prepare_boot_table/0 :: () -> 'ok'). +-spec(boot_with/1 :: (fun(() -> 'ok')) -> '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. -- cgit v1.2.1 From 43e19adfecf51a7854ac91b8b3c6dd7ecd05ca10 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 18 Feb 2014 15:01:19 +0000 Subject: Try and reduce the distance to default --- src/rabbit.erl | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/rabbit_boot.erl | 201 +++------------------------------------------------- 2 files changed, 207 insertions(+), 193 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 3690329f..5d9aaa5f 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 @@ -446,6 +475,159 @@ app_shutdown_order() -> Apps = ?APPS ++ rabbit_plugins:active(), 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 @@ -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 d0986442..4ceef0c7 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. -- cgit v1.2.1 From 04df9801211ae8defb19a296c4154d7e4594304d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 18 Feb 2014 15:10:32 +0000 Subject: Fix various oopses and reduce the distance to default some more --- src/rabbit.erl | 25 +++---------------------- src/rabbit_boot.erl | 2 -- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 5d9aaa5f..e7aaee51 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -22,7 +22,7 @@ stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/1, start_fhc/0]). - +-export([run_boot_steps/0, load_steps/1, run_step/3]). -export([start/2, stop/1]). -export([log_location/1]). %% for testing @@ -305,7 +305,7 @@ ensure_application_loaded() -> end. start() -> - rabbit_boot:boot_with( + boot_with( fun() -> %% We do not want to HiPE compile or upgrade %% mnesia after just restarting the app @@ -336,12 +336,6 @@ 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 @@ -479,7 +473,7 @@ app_shutdown_order() -> %% boot step logic run_boot_steps() -> - Steps = rabbit_boot:load_steps(boot), + Steps = load_steps(boot), [ok = run_boot_step(Step) || Step <- Steps], ok. @@ -668,19 +662,6 @@ 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 4ceef0c7..7fa5b0e6 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -20,7 +20,6 @@ -export([shutdown/1]). -export([start/1, stop/1]). -export([already_run/1, mark_complete/1]). --export([load_steps/1]). -ifdef(use_specs). @@ -30,7 +29,6 @@ -spec(shutdown/1 :: ([atom()]) -> 'ok'). -spec(start/1 :: ([atom()]) -> 'ok'). -spec(stop/1 :: ([atom()]) -> 'ok'). --spec(run_boot_steps/0 :: () -> 'ok'). -endif. -- cgit v1.2.1 From 17d987ebfa1f859bd0412ade9c589e2397e97247 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 21 Feb 2014 10:45:11 +0000 Subject: Cosmetic - reduce the distance to default a bit more --- src/app_utils.erl | 6 +++--- src/rabbit.erl | 57 ++++++++++++++++++++++++++----------------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 5eeb6924..e53909dd 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -69,6 +69,7 @@ stop_applications(Apps, ErrorHandler) -> ErrorHandler, Apps). + wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. @@ -80,9 +81,8 @@ app_dependency_order(RootApps, StripUnreachable) -> {App, _Desc, _Vsn} <- application:loaded_applications()]), try case StripUnreachable of - true -> digraph:del_vertices( - G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)); + true -> digraph:del_vertices(G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)); false -> ok end, digraph_utils:topsort(G) diff --git a/src/rabbit.erl b/src/rabbit.erl index e7aaee51..adfb934e 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -305,42 +305,40 @@ ensure_application_loaded() -> end. start() -> - boot_with( - fun() -> - %% We do not want to HiPE compile or upgrade - %% mnesia after just restarting the app - ok = ensure_application_loaded(), - ok = ensure_working_log_handlers(), - rabbit_node_monitor:prepare_cluster_status_files(), - rabbit_mnesia:check_cluster_consistency(), - ok = rabbit_boot:start(app_startup_order()), - ok = log_broker_started(rabbit_plugins:active()) - end). + boot_with(fun() -> + %% We do not want to HiPE compile or upgrade + %% mnesia after just restarting the app + ok = ensure_application_loaded(), + ok = ensure_working_log_handlers(), + rabbit_node_monitor:prepare_cluster_status_files(), + rabbit_mnesia:check_cluster_consistency(), + ok = rabbit_boot:start(app_startup_order()), + ok = log_broker_started(rabbit_plugins:active()) + end). boot() -> - boot_with( - fun() -> - ok = ensure_application_loaded(), - Success = maybe_hipe_compile(), - ok = ensure_working_log_handlers(), - warn_if_hipe_compilation_failed(Success), - rabbit_node_monitor:prepare_cluster_status_files(), - ok = rabbit_upgrade:maybe_upgrade_mnesia(), - %% It's important that the consistency check happens after - %% the upgrade, since if we are a secondary node the - %% primary node will have forgotten us - rabbit_mnesia:check_cluster_consistency(), - Plugins = rabbit_plugins:setup(), - ToBeLoaded = Plugins ++ ?APPS, - ok = rabbit_boot:start(ToBeLoaded), - ok = log_broker_started(Plugins) - end). + boot_with(fun() -> + ok = ensure_application_loaded(), + Success = maybe_hipe_compile(), + ok = ensure_working_log_handlers(), + warn_if_hipe_compilation_failed(Success), + rabbit_node_monitor:prepare_cluster_status_files(), + ok = rabbit_upgrade:maybe_upgrade_mnesia(), + %% It's important that the consistency check happens after + %% the upgrade, since if we are a secondary node the + %% primary node will have forgotten us + rabbit_mnesia:check_cluster_consistency(), + Plugins = rabbit_plugins:setup(), + ToBeLoaded = Plugins ++ ?APPS, + ok = rabbit_boot:start(ToBeLoaded), + ok = log_broker_started(Plugins) + end). 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 + case is_running() of true -> ok; false -> StartFun() end @@ -455,7 +453,6 @@ stop(_State) -> true -> rabbit_amqqueue:on_node_down(node()); false -> rabbit_table:clear_ram_only_tables() end, - ok = rabbit_boot:shutdown(), ok. %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 191ea8407b55f7ddd2473b762fa935159737d9ca Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 21 Feb 2014 12:50:14 +0000 Subject: Simply application:get_env calls and reduce distance to default again --- src/rabbit.erl | 10 +++++----- src/rabbit_alarm.erl | 6 ++---- src/rabbit_networking.erl | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index adfb934e..f2aa18a3 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -640,12 +640,12 @@ maybe_insert_default_data() -> end. insert_default_data() -> - {ok, DefaultUser} = application:get_env(rabbit, default_user), - {ok, DefaultPass} = application:get_env(rabbit, default_pass), - {ok, DefaultTags} = application:get_env(rabbit, default_user_tags), - {ok, DefaultVHost} = application:get_env(rabbit, default_vhost), + {ok, DefaultUser} = application:get_env(default_user), + {ok, DefaultPass} = application:get_env(default_pass), + {ok, DefaultTags} = application:get_env(default_user_tags), + {ok, DefaultVHost} = application:get_env(default_vhost), {ok, [DefaultConfigurePerm, DefaultWritePerm, DefaultReadPerm]} = - application:get_env(rabbit, default_permissions), + application:get_env(default_permissions), ok = rabbit_vhost:add(DefaultVHost), ok = rabbit_auth_backend_internal:add_user(DefaultUser, DefaultPass), ok = rabbit_auth_backend_internal:set_tags(DefaultUser, DefaultTags), diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index ac2fb42f..cd1d125b 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -53,8 +53,7 @@ start_link() -> start() -> ok = rabbit_sup:start_restartable_child(?MODULE), ok = gen_event:add_handler(?SERVER, ?MODULE, []), - {ok, MemoryWatermark} = application:get_env(rabbit, - vm_memory_high_watermark), + {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( vm_memory_monitor, [MemoryWatermark, fun (Alarm) -> @@ -62,8 +61,7 @@ start() -> set_alarm(Alarm) end, fun clear_alarm/1]), - {ok, DiskLimit} = application:get_env(rabbit, - disk_free_limit), + {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index b1016df1..42438790 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -125,12 +125,12 @@ boot() -> ok = boot_ssl(). boot_tcp() -> - {ok, TcpListeners} = application:get_env(rabbit, tcp_listeners), + {ok, TcpListeners} = application:get_env(tcp_listeners), [ok = start_tcp_listener(Listener) || Listener <- TcpListeners], ok. boot_ssl() -> - case application:get_env(rabbit, ssl_listeners) of + case application:get_env(ssl_listeners) of {ok, []} -> ok; {ok, SslListeners} -> -- cgit v1.2.1 From 01a781217dccc5161c8b381d0c05e24782bd884b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 28 Feb 2014 10:02:34 +0000 Subject: Fix plugin expansion during runtime configuration changes Blowing away the plugin expand directory when we make runtime changes is wrong and we don't need to strip away expanded plugins when they're disabled, since the directory is cleaned on each restart and leaving the files in place isn't an issue. --- src/rabbit_plugins.erl | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 6fe4c127..1799570d 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -38,20 +38,27 @@ %%---------------------------------------------------------------------------- -enable(Plugins) -> - setup(), - rabbit_boot:start(Plugins). +enable(Enabled) -> + prepare_plugins(Enabled), + rabbit_boot:start(Enabled). disable(Plugins) -> - setup(), rabbit_boot:stop(Plugins). %% @doc Prepares the file system and installs all enabled plugins. setup() -> - {ok, PluginDir} = application:get_env(rabbit, plugins_dir), {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + + %% Eliminate the contents of the destination directory + case delete_recursively(ExpandDir) of + ok -> ok; + {error, E1} -> throw({error, {cannot_delete_plugins_expand_dir, + [ExpandDir, E1]}}) + end, + {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file), - prepare_plugins(EnabledFile, PluginDir, ExpandDir). + Enabled = read_enabled(EnabledFile), + prepare_plugins(Enabled). %% @doc Lists the plugins which are currently running. active() -> @@ -114,9 +121,11 @@ dependencies(Reverse, Sources, AllPlugins) -> %%---------------------------------------------------------------------------- -prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> +prepare_plugins(Enabled) -> + {ok, PluginsDistDir} = application:get_env(rabbit, plugins_dir), + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + AllPlugins = list(PluginsDistDir), - Enabled = read_enabled(EnabledFile), ToUnpack = dependencies(false, Enabled, AllPlugins), ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), @@ -127,12 +136,6 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> [Missing]) end, - %% Eliminate the contents of the destination directory - case delete_recursively(ExpandDir) of - ok -> ok; - {error, E1} -> throw({error, {cannot_delete_plugins_expand_dir, - [ExpandDir, E1]}}) - end, case filelib:ensure_dir(ExpandDir ++ "/") of ok -> ok; {error, E2} -> throw({error, {cannot_create_plugins_expand_dir, -- cgit v1.2.1 From a75b074ca7aec248728949510550fefeed21b6ca Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Mar 2014 13:33:34 +0000 Subject: Publish plugin changes via rabbit_event --- src/app_utils.erl | 22 +++++++++++++++++++++- src/rabbit_plugins.erl | 12 ++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index e53909dd..eccf3d21 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -17,11 +17,13 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, - wait_for_applications/1, app_dependencies/1]). + wait_for_applications/1, app_dependencies/1, app_modules/1, + which_applications/0, update_running_apps/2]). -ifdef(use_specs). -type error_handler() :: fun((atom(), any()) -> 'ok'). +-type diff() :: [atom()]. -spec load_applications([atom()]) -> 'ok'. -spec start_applications([atom()]) -> 'ok'. @@ -31,12 +33,30 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. +-spec update_running_apps(fun (() -> 'ok'), + fun ((diff()) -> 'ok')) -> 'ok'. +-spec which_applications() -> [atom()]. +-spec app_modules(atom()) -> [module()]. -endif. %%--------------------------------------------------------------------------- %% Public API +update_running_apps(MakeChanges, WithChanges) -> + Old = sets:from_list(which_applications()), + MakeChanges(), + New = sets:from_list(which_applications()), + Diff = sets:to_list(sets:subtract(New, Old)), + WithChanges(Diff). + +which_applications() -> + [App || {App, _, _} <- rabbit_misc:which_applications()]. + +app_modules(App) -> + {ok, Modules} = application:get_key(App, modules), + Modules. + load_applications(Apps) -> load_applications(queue:from_list(Apps), sets:new()), ok. diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 1799570d..add818e3 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -40,10 +40,18 @@ enable(Enabled) -> prepare_plugins(Enabled), - rabbit_boot:start(Enabled). + app_utils:update_running_apps( + fun() -> rabbit_boot:start(Enabled) end, + fun(Diff) -> + ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]) + end). disable(Plugins) -> - rabbit_boot:stop(Plugins). + app_utils:update_running_apps( + fun() -> rabbit_boot:stop(Plugins) end, + fun(Diff) -> + ok = rabbit_event:notify(plugins_changed, [{disabled, Diff}]) + end). %% @doc Prepares the file system and installs all enabled plugins. setup() -> -- cgit v1.2.1 From e282caeebce551b1fa7d4f121e8312679e7fd21b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Mar 2014 11:37:56 +0000 Subject: Reduce the distance to default a bit more --- src/app_utils.erl | 4 ++-- src/rabbit.erl | 67 ++++++++++++++++++++++++++++------------------------- src/rabbit_boot.erl | 14 ----------- 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index eccf3d21..4edee86f 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -101,8 +101,8 @@ app_dependency_order(RootApps, StripUnreachable) -> {App, _Desc, _Vsn} <- application:loaded_applications()]), try case StripUnreachable of - true -> digraph:del_vertices(G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)); + true -> digraph:del_vertices(G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)); false -> ok end, digraph_utils:topsort(G) diff --git a/src/rabbit.erl b/src/rabbit.erl index f2aa18a3..2070713e 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -305,36 +305,36 @@ ensure_application_loaded() -> end. start() -> - boot_with(fun() -> - %% We do not want to HiPE compile or upgrade - %% mnesia after just restarting the app - ok = ensure_application_loaded(), - ok = ensure_working_log_handlers(), - rabbit_node_monitor:prepare_cluster_status_files(), - rabbit_mnesia:check_cluster_consistency(), - ok = rabbit_boot:start(app_startup_order()), - ok = log_broker_started(rabbit_plugins:active()) - end). + start_it(fun() -> + %% We do not want to HiPE compile or upgrade + %% mnesia after just restarting the app + ok = ensure_application_loaded(), + ok = ensure_working_log_handlers(), + rabbit_node_monitor:prepare_cluster_status_files(), + rabbit_mnesia:check_cluster_consistency(), + ok = rabbit_boot:start(app_startup_order()), + ok = log_broker_started(rabbit_plugins:active()) + end). boot() -> - boot_with(fun() -> - ok = ensure_application_loaded(), - Success = maybe_hipe_compile(), - ok = ensure_working_log_handlers(), - warn_if_hipe_compilation_failed(Success), - rabbit_node_monitor:prepare_cluster_status_files(), - ok = rabbit_upgrade:maybe_upgrade_mnesia(), - %% It's important that the consistency check happens after - %% the upgrade, since if we are a secondary node the - %% primary node will have forgotten us - rabbit_mnesia:check_cluster_consistency(), - Plugins = rabbit_plugins:setup(), - ToBeLoaded = Plugins ++ ?APPS, - ok = rabbit_boot:start(ToBeLoaded), - ok = log_broker_started(Plugins) - end). - -boot_with(StartFun) -> + start_it(fun() -> + ok = ensure_application_loaded(), + Success = maybe_hipe_compile(), + ok = ensure_working_log_handlers(), + warn_if_hipe_compilation_failed(Success), + rabbit_node_monitor:prepare_cluster_status_files(), + ok = rabbit_upgrade:maybe_upgrade_mnesia(), + %% It's important that the consistency check happens after + %% the upgrade, since if we are a secondary node the + %% primary node will have forgotten us + rabbit_mnesia:check_cluster_consistency(), + Plugins = rabbit_plugins:setup(), + ToBeLoaded = Plugins ++ ?APPS, + ok = rabbit_boot:start(ToBeLoaded), + ok = log_broker_started(Plugins) + end). + +start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), case catch register(rabbit_boot, Marker) of true -> try @@ -358,8 +358,13 @@ boot_with(StartFun) -> end. stop() -> + Apps = app_shutdown_order(), + case whereis(?MODULE) of + undefined -> ok; + _ -> app_utils:wait_for_applications(Apps) + end, rabbit_log:info("Stopping RabbitMQ~n"), - rabbit_boot:shutdown(app_shutdown_order()). + ok = app_utils:stop_applications(Apps). stop_and_halt() -> try @@ -604,8 +609,8 @@ boot_error(Reason, Stacktrace) -> boot_error(Reason, Fmt, Args, Stacktrace). -ifdef(use_specs). --spec(boot_error/4 :: (term(), string(), [any()], not_available | [tuple()]) - -> no_return()). +-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); diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 7fa5b0e6..0b7b71a3 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -17,7 +17,6 @@ -module(rabbit_boot). -export([prepare_boot_table/0]). --export([shutdown/1]). -export([start/1, stop/1]). -export([already_run/1, mark_complete/1]). @@ -26,7 +25,6 @@ -spec(prepare_boot_table/0 :: () -> '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'). @@ -51,15 +49,6 @@ prepare_boot_table() -> ets:new(?MODULE, [named_table, public, ordered_set]). -shutdown(Apps) -> - case whereis(?MODULE) of - undefined -> ok; - _ -> await_startup(Apps) - end, - %% TODO: put this back in somewhere sensible... - %% rabbit_log:info("Stopping RabbitMQ~n"), - ok = app_utils:stop_applications(Apps). - start(Apps) -> force_reload(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), @@ -136,9 +125,6 @@ load_mod(Mod) -> _ -> code:load_file(Mod) end. -await_startup(Apps) -> - app_utils:wait_for_applications(Apps). - handle_app_error(Term) -> fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> throw({Term, App, ExitReason}); -- cgit v1.2.1 From c944edb4f5306068a66c19543853a74e5ee5667c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Mar 2014 12:15:35 +0000 Subject: Inline some more and get closer still to default --- src/rabbit.erl | 24 +++++++++++++++++++++--- src/rabbit_boot.erl | 24 ++++-------------------- src/rabbit_plugins.erl | 2 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 2070713e..9644f75e 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([log_location/1]). %% for testing %%--------------------------------------------------------------------------- @@ -242,6 +242,7 @@ -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. @@ -312,7 +313,7 @@ start() -> ok = ensure_working_log_handlers(), rabbit_node_monitor:prepare_cluster_status_files(), rabbit_mnesia:check_cluster_consistency(), - ok = rabbit_boot:start(app_startup_order()), + ok = start_apps(app_startup_order()), ok = log_broker_started(rabbit_plugins:active()) end). @@ -330,10 +331,27 @@ boot() -> rabbit_mnesia:check_cluster_consistency(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, - ok = rabbit_boot:start(ToBeLoaded), + ok = start_apps(ToBeLoaded), ok = log_broker_started(Plugins) end). +handle_app_error(Term) -> + fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> + throw({Term, App, ExitReason}); + (App, Reason) -> + throw({Term, App, Reason}) + end. + +start_apps(Apps) -> + rabbit_boot:force_reload(Apps), + StartupApps = app_utils:app_dependency_order(Apps, false), + case whereis(?MODULE) of + undefined -> rabbit:run_boot_steps(); + _ -> ok + end, + ok = app_utils:start_applications(StartupApps, + handle_app_error(could_not_start)). + start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), case catch register(rabbit_boot, Marker) of diff --git a/src/rabbit_boot.erl b/src/rabbit_boot.erl index 0b7b71a3..e9bf5457 100644 --- a/src/rabbit_boot.erl +++ b/src/rabbit_boot.erl @@ -17,7 +17,8 @@ -module(rabbit_boot). -export([prepare_boot_table/0]). --export([start/1, stop/1]). +-export([stop/1]). +-export([force_reload/1]). -export([already_run/1, mark_complete/1]). -ifdef(use_specs). @@ -25,8 +26,8 @@ -spec(prepare_boot_table/0 :: () -> 'ok'). -spec(already_run/1 :: (atom()) -> boolean()). -spec(mark_complete/1 :: (atom()) -> 'ok'). --spec(start/1 :: ([atom()]) -> 'ok'). -spec(stop/1 :: ([atom()]) -> 'ok'). +-spec(force_reload/1 :: ([atom()]) -> 'ok'). -endif. @@ -49,20 +50,10 @@ prepare_boot_table() -> ets:new(?MODULE, [named_table, public, ordered_set]). -start(Apps) -> - force_reload(Apps), - StartupApps = app_utils:app_dependency_order(Apps, false), - case whereis(?MODULE) of - undefined -> rabbit:run_boot_steps(); - _ -> ok - end, - ok = app_utils:start_applications(StartupApps, - handle_app_error(could_not_start)). - stop(Apps) -> try ok = app_utils:stop_applications( - Apps, handle_app_error(error_during_shutdown)) + Apps, rabbit:handle_app_error(error_during_shutdown)) after BootSteps = rabbit:load_steps(boot), ToDelete = [Step || {App, _, _}=Step <- BootSteps, @@ -125,13 +116,6 @@ load_mod(Mod) -> _ -> code:load_file(Mod) end. -handle_app_error(Term) -> - fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> - throw({Term, App, ExitReason}); - (App, Reason) -> - throw({Term, App, Reason}) - end. - run_cleanup_step({_, StepName, Attributes}) -> rabbit:run_step(StepName, Attributes, cleanup). diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index add818e3..e15ae0b1 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -41,7 +41,7 @@ enable(Enabled) -> prepare_plugins(Enabled), app_utils:update_running_apps( - fun() -> rabbit_boot:start(Enabled) end, + fun() -> rabbit:start_apps(Enabled) end, fun(Diff) -> ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]) end). -- cgit v1.2.1 From 5f5a8f2863c325cc7d8810d8b19ff0f1192a1c08 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Mar 2014 14:02:59 +0000 Subject: Fix an oops in rabbit:stop/0 and apply consistent parameter names in r_plugins --- src/rabbit.erl | 2 +- src/rabbit_plugins.erl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 9644f75e..82394cd9 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -377,7 +377,7 @@ start_it(StartFun) -> stop() -> Apps = app_shutdown_order(), - case whereis(?MODULE) of + case whereis(rabbit_boot) of undefined -> ok; _ -> app_utils:wait_for_applications(Apps) end, diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index e15ae0b1..6572a625 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -38,10 +38,10 @@ %%---------------------------------------------------------------------------- -enable(Enabled) -> - prepare_plugins(Enabled), +enable(Plugins) -> + prepare_plugins(Plugins), app_utils:update_running_apps( - fun() -> rabbit:start_apps(Enabled) end, + fun() -> rabbit:start_apps(Plugins) end, fun(Diff) -> ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]) end). -- cgit v1.2.1 From aea7d7763d89bd3395147f2c0d056d4301ef9cc0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Mar 2014 13:49:48 +0000 Subject: We missed another change when moving code back into rabbit.erl --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 82394cd9..46838364 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -345,7 +345,7 @@ handle_app_error(Term) -> start_apps(Apps) -> rabbit_boot:force_reload(Apps), StartupApps = app_utils:app_dependency_order(Apps, false), - case whereis(?MODULE) of + case whereis(rabbit_boot) of undefined -> rabbit:run_boot_steps(); _ -> ok end, -- cgit v1.2.1 From 5f8d433a545c7ec150fc69c9f94be67ed6219325 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 13 Mar 2014 22:49:16 +0000 Subject: 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. --- src/rabbit.erl | 137 ++++++++++++++++++++++++++++++++----------------- src/rabbit_boot.erl | 127 --------------------------------------------- src/rabbit_plugins.erl | 2 +- 3 files changed, 91 insertions(+), 175 deletions(-) delete mode 100644 src/rabbit_boot.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index 46838364..c703fedb 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 e9bf5457..00000000 --- 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 6572a625..1f36ce4c 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). -- cgit v1.2.1 From a31dcbe5765b6ce8aae765e2683e012beae36e86 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 14 Mar 2014 14:58:20 +0000 Subject: Improve synchronisation between plugins file and running broker Since multiple brokers (on a host) may share the enabled-plugins file, we may get out of sync. Diff any changes stemming from enable/disable operations against the plugins actively running on the broker. Extend the 'list' operation to display plugins that are [A] active but not explicitly (or implicitly) enabled in the file and [I], those that are explicitly (or implicitly) enabled but not actively running in the broker. --- src/rabbit_plugins_main.erl | 86 +++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 408fc4e1..e7f9c022 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -24,19 +24,24 @@ -define(MINIMAL_OPT, "-m"). -define(ENABLED_OPT, "-E"). -define(ENABLED_ALL_OPT, "-e"). +-define(ACTIVE_ONLY_OPT, "-A"). +-define(INACTIVE_ONLY_OPT, "-I"). -define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). -define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). -define(ENABLED_DEF, {?ENABLED_OPT, flag}). -define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). +-define(ACTIVE_ONLY_DEF, {?ACTIVE_ONLY_OPT, flag}). +-define(INACTIVE_ONLY_DEF, {?INACTIVE_ONLY_OPT, flag}). -define(RPC_TIMEOUT, infinity). -define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]). -define(COMMANDS, - [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, + [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, + ?ENABLED_ALL_DEF, ?ACTIVE_ONLY_DEF, ?INACTIVE_ONLY_DEF]}, enable, disable]). @@ -111,8 +116,8 @@ parse_arguments(CmdLine, NodeStr) -> action(list, Node, [], Opts, PluginsFile, PluginsDir) -> action(list, Node, [".*"], Opts, PluginsFile, PluginsDir); -action(list, _Node, [Pat], Opts, PluginsFile, PluginsDir) -> - format_plugins(Pat, Opts, PluginsFile, PluginsDir); +action(list, Node, [Pat], Opts, PluginsFile, PluginsDir) -> + format_plugins(Node, Pat, Opts, PluginsFile, PluginsDir); action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> case ToEnable0 of @@ -125,7 +130,16 @@ action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), - NewEnabled = lists:usort(Enabled ++ ToEnable), + ExplicitlyEnabled = lists:usort(Enabled ++ ToEnable), + NewEnabled = + case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of + {badrpc, _} -> ExplicitlyEnabled; + [] -> ExplicitlyEnabled; + ActiveList -> EnabledSet = sets:from_list(ExplicitlyEnabled), + ActiveSet = sets:from_list(ActiveList), + Intersect = sets:intersection(EnabledSet, ActiveSet), + sets:to_list(sets:subtract(EnabledSet, Intersect)) + end, NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), MissingDeps = (NewImplicitlyEnabled -- plugin_names(AllPlugins)) -- Missing, @@ -137,11 +151,11 @@ action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> fmt_missing("plugins", Missing) ++ fmt_missing("dependencies", MissingDeps)}) end, - write_enabled_plugins(PluginsFile, NewEnabled), - case NewEnabled -- ImplicitlyEnabled of + write_enabled_plugins(PluginsFile, ExplicitlyEnabled), + case NewEnabled -- (ImplicitlyEnabled -- ExplicitlyEnabled) of [] -> io:format("Plugin configuration unchanged.~n"); _ -> print_list("The following plugins have been enabled:", - NewImplicitlyEnabled -- ImplicitlyEnabled), + NewEnabled), action_change(Node, enable, NewEnabled) end; @@ -160,11 +174,17 @@ action(disable, Node, ToDisable0, _Opts, PluginsFile, PluginsDir) -> Missing) end, ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins), + Active = + case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of + {badrpc, _} -> Enabled; + [] -> Enabled; + ActiveList -> ActiveList + end, NewEnabled = Enabled -- ToDisableDeps, - case length(Enabled) =:= length(NewEnabled) of + case length(Active) =:= length(NewEnabled) of true -> io:format("Plugin configuration unchanged.~n"); false -> ImplicitlyEnabled = - rabbit_plugins:dependencies(false, Enabled, AllPlugins), + rabbit_plugins:dependencies(false, Active, AllPlugins), NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), @@ -185,7 +205,7 @@ usage() -> rabbit_misc:quit(1). %% Pretty print a list of plugins. -format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> +format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> Verbose = proplists:get_bool(?VERBOSE_OPT, Opts), Minimal = proplists:get_bool(?MINIMAL_OPT, Opts), Format = case {Verbose, Minimal} of @@ -197,7 +217,14 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> end, OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), - + OnlyActive = proplists:get_bool(?ACTIVE_ONLY_OPT, Opts), + OnlyInactive = proplists:get_bool(?INACTIVE_ONLY_OPT, Opts), + + ActivePlugins = case rpc:call(Node, rabbit_plugins, active, + [], ?RPC_TIMEOUT) of + {badrpc, _} -> []; + Active -> Active + end, AvailablePlugins = rabbit_plugins:list(PluginsDir), EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), EnabledImplicitly = @@ -211,29 +238,44 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> Plugin = #plugin{name = Name} <- AvailablePlugins ++ Missing, re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, if OnlyEnabled -> lists:member(Name, EnabledExplicitly); - OnlyEnabledAll -> (lists:member(Name, - EnabledExplicitly) or - lists:member(Name, EnabledImplicitly)); + OnlyEnabledAll -> is_enabled(Name, EnabledExplicitly, + EnabledImplicitly); + OnlyActive -> (lists:member(Name, ActivePlugins) + andalso not + (is_enabled(Name, EnabledExplicitly, + EnabledImplicitly))); + OnlyInactive -> (is_enabled(Name, EnabledExplicitly, + EnabledImplicitly) + andalso not + lists:member(Name, ActivePlugins)); true -> true end], Plugins1 = usort_plugins(Plugins), MaxWidth = lists:max([length(atom_to_list(Name)) || #plugin{name = Name} <- Plugins1] ++ [0]), - [format_plugin(P, EnabledExplicitly, EnabledImplicitly, + [format_plugin(P, EnabledExplicitly, EnabledImplicitly, ActivePlugins, plugin_names(Missing), Format, MaxWidth) || P <- Plugins1], ok. +is_enabled(Name, EnabledExplicitly, EnabledImplicitly) -> + lists:member(Name,EnabledExplicitly) or + lists:member(Name, EnabledImplicitly). + format_plugin(#plugin{name = Name, version = Version, description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Missing, - Format, MaxWidth) -> + EnabledExplicitly, EnabledImplicitly, Active, + Missing, Format, MaxWidth) -> Glyph = case {lists:member(Name, EnabledExplicitly), lists:member(Name, EnabledImplicitly), - lists:member(Name, Missing)} of - {true, false, false} -> "[E]"; - {false, true, false} -> "[e]"; - {_, _, true} -> "[!]"; - _ -> "[ ]" + lists:member(Name, Missing), + lists:member(Name, Active)} of + {true, false, false, false} -> "[I]"; + {false, true, false, false} -> "[i]"; + {false, false, false, true} -> "[A]"; + {true, false, false, true} -> "[E]"; + {false, true, false, true} -> "[e]"; + {_, _, true, _} -> "[!]"; + _ -> "[ ]" end, Opt = fun (_F, A, A) -> ok; ( F, A, _) -> io:format(F, [A]) -- cgit v1.2.1 From 2aeb2d0cefa24540aa5b6e902ed89a7b48ee4400 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 14 Mar 2014 16:24:13 +0000 Subject: Update rabbitmq-plugins usage --- docs/rabbitmq-plugins.1.xml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index 8ecb4fc8..f860e9c3 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -69,7 +69,7 @@ - list -v -m -E -e pattern + list -v -m -E -e -A -I pattern @@ -90,6 +90,16 @@ Show only explicitly or implicitly enabled plugins. + + -A + Show active plugins (running on the broker) that + are not explicitly or implicitly enabled. + + + -I + Show in-active plugins (not running on the broker) that + are explicitly or implicitly enabled. + pattern Pattern to filter the plugin names by. @@ -102,7 +112,11 @@ enabled, [E] to indicate that it is explicitly enabled, [e] to indicate that it is implicitly enabled, and [!] to indicate that it is enabled but missing and thus not - operational. + operational. When a plugin is enabled but not currently + active (i.e., running) in the currently targetted broker, + [A] is shown, whilst a plugin which is active/running but + not currently configured to be enabled is indicated by + an [I]. If the optional pattern is given, only plugins whose -- cgit v1.2.1 From 3de081adf60d9348f73b3401e81b1570a2419c2e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Mar 2014 15:58:47 +0000 Subject: Inline app_utils:update_running_apps/2 --- src/app_utils.erl | 12 +----------- src/rabbit.erl | 7 ++++--- src/rabbit_plugins.erl | 14 ++++---------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 4edee86f..56d49899 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -18,12 +18,11 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, wait_for_applications/1, app_dependencies/1, app_modules/1, - which_applications/0, update_running_apps/2]). + which_applications/0]). -ifdef(use_specs). -type error_handler() :: fun((atom(), any()) -> 'ok'). --type diff() :: [atom()]. -spec load_applications([atom()]) -> 'ok'. -spec start_applications([atom()]) -> 'ok'. @@ -33,8 +32,6 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. --spec update_running_apps(fun (() -> 'ok'), - fun ((diff()) -> 'ok')) -> 'ok'. -spec which_applications() -> [atom()]. -spec app_modules(atom()) -> [module()]. @@ -43,13 +40,6 @@ %%--------------------------------------------------------------------------- %% Public API -update_running_apps(MakeChanges, WithChanges) -> - Old = sets:from_list(which_applications()), - MakeChanges(), - New = sets:from_list(which_applications()), - Diff = sets:to_list(sets:subtract(New, Old)), - WithChanges(Diff). - which_applications() -> [App || {App, _, _} <- rabbit_misc:which_applications()]. diff --git a/src/rabbit.erl b/src/rabbit.erl index 062b9355..3d043b50 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -312,7 +312,7 @@ start() -> ok = ensure_working_log_handlers(), rabbit_node_monitor:prepare_cluster_status_files(), rabbit_mnesia:check_cluster_consistency(), - ok = start_apps(app_startup_order()), + start_apps(app_startup_order()), ok = log_broker_started(rabbit_plugins:active()) end). @@ -330,7 +330,7 @@ boot() -> rabbit_mnesia:check_cluster_consistency(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, - ok = start_apps(ToBeLoaded), + start_apps(ToBeLoaded), ok = log_broker_started(Plugins) end). @@ -349,7 +349,8 @@ start_apps(Apps) -> _ -> ok end, ok = app_utils:start_applications(StartupApps, - handle_app_error(could_not_start)). + handle_app_error(could_not_start)), + StartupApps. start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 1f36ce4c..699cf100 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -40,18 +40,12 @@ enable(Plugins) -> prepare_plugins(Plugins), - app_utils:update_running_apps( - fun() -> rabbit:start_apps(Plugins) end, - fun(Diff) -> - ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]) - end). + Diff = rabbit:start_apps(Plugins), + ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]). disable(Plugins) -> - app_utils:update_running_apps( - fun() -> rabbit:stop_apps(Plugins) end, - fun(Diff) -> - ok = rabbit_event:notify(plugins_changed, [{disabled, Diff}]) - end). + Diff = rabbit:stop_apps(Plugins), + ok = rabbit_event:notify(plugins_changed, [{disabled, Plugins}]). %% @doc Prepares the file system and installs all enabled plugins. setup() -> -- cgit v1.2.1 From c9fe82719f7ed3329bc3356ac36ae04c54db1156 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Mar 2014 16:03:26 +0000 Subject: Calculate enabled vs. active correctly for 'list' operations --- src/rabbit_plugins_main.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index e7f9c022..c9d4b73f 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -220,16 +220,16 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> OnlyActive = proplists:get_bool(?ACTIVE_ONLY_OPT, Opts), OnlyInactive = proplists:get_bool(?INACTIVE_ONLY_OPT, Opts), + AvailablePlugins = rabbit_plugins:list(PluginsDir), + EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), + AllEnabled = rabbit_plugins:dependencies(false, EnabledExplicitly, + AvailablePlugins), + EnabledImplicitly = AllEnabled -- EnabledExplicitly, ActivePlugins = case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> []; + {badrpc, _} -> AllEnabled; Active -> Active end, - AvailablePlugins = rabbit_plugins:list(PluginsDir), - EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), - EnabledImplicitly = - rabbit_plugins:dependencies(false, EnabledExplicitly, - AvailablePlugins) -- EnabledExplicitly, Missing = [#plugin{name = Name, dependencies = []} || Name <- ((EnabledExplicitly ++ EnabledImplicitly) -- plugin_names(AvailablePlugins))], -- cgit v1.2.1 From 8fb1d1ea7f3b35ad9e1fb89548d1af91db275ef9 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Mar 2014 19:52:29 +0000 Subject: Refactor (inline a little bit more) app_utils:which_applications/0 => inlined, rabbit_misc:all_module_attributes{_with_app} => unified --- src/app_utils.erl | 7 +------ src/rabbit.erl | 4 ++-- src/rabbit_misc.erl | 39 ++++++++++----------------------------- src/rabbit_version.erl | 5 +++-- 4 files changed, 16 insertions(+), 39 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 56d49899..270cdc83 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -17,8 +17,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, - wait_for_applications/1, app_dependencies/1, app_modules/1, - which_applications/0]). + wait_for_applications/1, app_dependencies/1, app_modules/1]). -ifdef(use_specs). @@ -32,7 +31,6 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. --spec which_applications() -> [atom()]. -spec app_modules(atom()) -> [module()]. -endif. @@ -40,9 +38,6 @@ %%--------------------------------------------------------------------------- %% Public API -which_applications() -> - [App || {App, _, _} <- rabbit_misc:which_applications()]. - app_modules(App) -> {ok, Modules} = application:get_key(App, modules), Modules. diff --git a/src/rabbit.erl b/src/rabbit.erl index 3d043b50..9b827d83 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -543,8 +543,8 @@ run_step(StepName, Attributes, AttributeName) -> end. load_steps(BaseApps) -> - Apps = BaseApps -- app_utils:which_applications(), %% exclude running apps - StepAttrs = rabbit_misc:all_module_attributes_with_app(rabbit_boot_step), + Apps = BaseApps -- [App || {App, _, _} <- rabbit_misc:which_applications()], + StepAttrs = rabbit_misc:all_module_attributes(rabbit_boot_step), {AllSteps, StepsDict} = lists:foldl( fun({AppName, Mod, Steps}, {AccSteps, AccDict}) -> diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 15f6ff43..9aa433b6 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -51,7 +51,6 @@ -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). -export([gb_trees_fold/3, gb_trees_foreach/2]). -export([parse_arguments/3]). --export([all_module_attributes_with_app/1]). -export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). -export([const/1]). @@ -210,8 +209,7 @@ [string()]) -> {'ok', {atom(), [{string(), string()}], [string()]}} | 'no_command'). --spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]). --spec(all_module_attributes_with_app/1 :: +-spec(all_module_attributes/1 :: (atom()) -> [{atom(), atom(), [term()]}]). -spec(build_acyclic_graph/3 :: (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) @@ -849,38 +847,21 @@ module_attributes(Module) -> V end. -all_module_attributes_with_app(Name) -> - find_module_attributes( - fun(App, Modules) -> - [{App, Module} || Module <- Modules] - end, - fun ({App, Module}, Acc) -> - case lists:append([Atts || {N, Atts} <- module_attributes(Module), - N =:= Name]) of - [] -> Acc; - Atts -> [{App, Module, Atts} | Acc] - end - end). - all_module_attributes(Name) -> - find_module_attributes( - fun(_App, Modules) -> Modules end, - fun (Module, Acc) -> - case lists:append([Atts || {N, Atts} <- module_attributes(Module), - N =:= Name]) of - [] -> Acc; - Atts -> [{Module, Atts} | Acc] - end - end). - -find_module_attributes(Generator, Fold) -> Targets = lists:usort( lists:append( - [Generator(App, Modules) || + [[{App, Module} || Module <- Modules] || {App, _, _} <- application:loaded_applications(), {ok, Modules} <- [application:get_key(App, modules)]])), - lists:foldl(Fold, [], Targets). + lists:foldl( + fun ({App, Module}, Acc) -> + case lists:append([Atts || {N, Atts} <- module_attributes(Module), + N =:= Name]) of + [] -> Acc; + Atts -> [{App, Module, Atts} | Acc] + end + end, [], Targets). build_acyclic_graph(VertexFun, EdgeFun, Graph) -> G = digraph:new([acyclic]), diff --git a/src/rabbit_version.erl b/src/rabbit_version.erl index c629180e..a9efdb49 100644 --- a/src/rabbit_version.erl +++ b/src/rabbit_version.erl @@ -113,10 +113,11 @@ upgrades_required(Scope) -> %% ------------------------------------------------------------------- with_upgrade_graph(Fun, Scope) -> + Attrs = rabbit_misc:all_module_attributes(rabbit_upgrade), case rabbit_misc:build_acyclic_graph( fun (Module, Steps) -> vertices(Module, Steps, Scope) end, fun (Module, Steps) -> edges(Module, Steps, Scope) end, - rabbit_misc:all_module_attributes(rabbit_upgrade)) of + [{Mod, Steps} || {_, Mod, Steps} <- Attrs]) of {ok, G} -> try Fun(G) after @@ -161,7 +162,7 @@ heads(G) -> categorise_by_scope(Version) when is_list(Version) -> Categorised = - [{Scope, Name} || {_Module, Attributes} <- + [{Scope, Name} || {_App, _Module, Attributes} <- rabbit_misc:all_module_attributes(rabbit_upgrade), {Name, Scope, _Requires} <- Attributes, lists:member(Name, Version)], -- cgit v1.2.1 From b149facf3ef4b29721353a052b3c5de5ec314df9 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Mar 2014 21:08:47 +0000 Subject: A slightly nicer (more explanatory) UI for rabbitmq-plugins --- src/rabbit_plugins_main.erl | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index c9d4b73f..3976cc45 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -326,12 +326,35 @@ action_change(Node, Action, Targets) -> rpc_call(Node, rabbit_plugins, Action, [Targets]). rpc_call(Node, Mod, Action, Args) -> - case rpc:call(Node, Mod, Action, Args, ?RPC_TIMEOUT) of - {badrpc, nodedown} -> io:format("Plugin configuration has changed.~n"); - ok -> io:format("Plugin(s) ~pd.~n", [Action]); - Error -> io:format("Unable to ~p plugin(s). " - "Please restart the broker " - "to apply your changes.~nError: ~p~n", - [Action, Error]) + case net_adm:ping(Node) of + pong -> io:format("Changing plugin configuration on ~p.", [Node]), + AsyncKey = rpc:async_call(Node, Mod, Action, Args), + rpc_progress(AsyncKey, Node, Action); + pang -> io:format("Plugin configuration has changed. " + "Plugins were not ~p since node is down.~n" + "Please start the broker to apply " + "your changes.~n", + [case Action of + enable -> started; + disable -> stopped + end]) + + end. + +rpc_progress(Key, Node, Action) -> + case rpc:nb_yield(Key, 100) of + timeout -> io:format("."), + rpc_progress(Key, Node, Action); + {value, {badrpc, nodedown}} -> + io:format(". error: Unable to contact ~p.~n ", [Node]), + io:format("Please start the broker to apply " + "your changes.~n"); + {value, ok} -> + io:format(". done: Plugin(s) ~pd.~n", [Action]); + {value, Error} -> + io:format(". error: Unable to ~p plugin(s).~n" + "Please restart the broker to apply your changes.~n" + "Error: ~p~n", + [Action, Error]) end. -- cgit v1.2.1 From 19767b76842b73006d6840950d572a777077eadf Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 17 Mar 2014 21:09:46 +0000 Subject: English --- src/rabbit_plugins_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 3976cc45..42fb23b7 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -331,7 +331,7 @@ rpc_call(Node, Mod, Action, Args) -> AsyncKey = rpc:async_call(Node, Mod, Action, Args), rpc_progress(AsyncKey, Node, Action); pang -> io:format("Plugin configuration has changed. " - "Plugins were not ~p since node is down.~n" + "Plugins were not ~p since the node is down.~n" "Please start the broker to apply " "your changes.~n", [case Action of -- cgit v1.2.1 From aa988e671de30e51f5873e25382ee7ec4e9c090c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 18 Mar 2014 22:16:40 +0000 Subject: Get rid of app_utils:app_modules/1 --- src/app_utils.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 2efbb1f1..ce07ac50 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -17,7 +17,7 @@ -export([load_applications/1, start_applications/1, start_applications/2, stop_applications/1, stop_applications/2, app_dependency_order/2, - wait_for_applications/1, app_dependencies/1, app_modules/1]). + wait_for_applications/1, app_dependencies/1]). -ifdef(use_specs). @@ -31,17 +31,12 @@ -spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -spec app_dependencies(atom()) -> [atom()]. --spec app_modules(atom()) -> [module()]. -endif. %%--------------------------------------------------------------------------- %% Public API -app_modules(App) -> - {ok, Modules} = application:get_key(App, modules), - Modules. - load_applications(Apps) -> load_applications(queue:from_list(Apps), sets:new()), ok. -- cgit v1.2.1 From b2db9e0e6884ebf9e7654d8aad1a13529843a703 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Mar 2014 15:43:12 +0000 Subject: If we refactor rabbit_misc:build_acyclic_graph so that we don't assume the arity of the functions we pass in, then it's easy to build the complete graph then filter it by application, rather than the other way round - avoiding rather a lot of work. --- src/app_utils.erl | 4 +-- src/rabbit.erl | 66 ++++++++------------------------------------------ src/rabbit_misc.erl | 8 +++--- src/rabbit_plugins.erl | 4 +-- src/rabbit_version.erl | 4 +-- 5 files changed, 20 insertions(+), 66 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index ce07ac50..e321888d 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -75,8 +75,8 @@ wait_for_applications(Apps) -> app_dependency_order(RootApps, StripUnreachable) -> {ok, G} = rabbit_misc:build_acyclic_graph( - fun (App, _Deps) -> [{App, App}] end, - fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, + fun ({App, _Deps}) -> [{App, App}] end, + fun ({App, Deps}) -> [{Dep, App} || Dep <- Deps] end, [{App, app_dependencies(App)} || {App, _Desc, _Vsn} <- application:loaded_applications()]), try diff --git a/src/rabbit.erl b/src/rabbit.erl index 16106145..07b21e50 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -531,11 +531,17 @@ run_boot_steps() -> run_boot_steps([App || {App, _, _} <- application:loaded_applications()]). run_boot_steps(Apps) -> - Steps = load_steps(Apps), + Steps = find_steps(Apps), [ok = run_step(StepName, Attributes, mfa) || {_, StepName, Attributes} <- Steps], ok. +find_steps(BaseApps) -> + Apps = BaseApps -- [App || {App, _, _} <- rabbit_misc:which_applications()], + FullBoot = sort_boot_steps( + rabbit_misc:all_module_attributes(rabbit_boot_step)), + [Step || {App, _, _} = Step <- FullBoot, lists:member(App, Apps)]. + run_step(StepName, Attributes, AttributeName) -> case [MFA || {Key, MFA} <- Attributes, Key =:= AttributeName] of @@ -555,62 +561,10 @@ run_step(StepName, Attributes, AttributeName) -> ok end. -load_steps(BaseApps) -> - Apps = BaseApps -- [App || {App, _, _} <- rabbit_misc:which_applications()], - StepAttrs = rabbit_misc:all_module_attributes(rabbit_boot_step), - {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}) -> +vertices({AppName, _Module, Steps}) -> [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps]. -edges(_Module, {_AppName, Steps}) -> +edges({_AppName, _Module, Steps}) -> [case Key of requires -> {StepName, OtherStep}; enables -> {OtherStep, StepName} @@ -619,7 +573,7 @@ edges(_Module, {_AppName, Steps}) -> Key =:= requires orelse Key =:= enables]. sort_boot_steps(UnsortedSteps) -> - case rabbit_misc:build_acyclic_graph(fun vertices/2, fun edges/2, + case rabbit_misc:build_acyclic_graph(fun vertices/1, fun edges/1, UnsortedSteps) of {ok, G} -> %% Use topological sort to find a consistent ordering (if diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 86616632..eb79fca6 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -871,13 +871,13 @@ build_acyclic_graph(VertexFun, EdgeFun, Graph) -> [case digraph:vertex(G, Vertex) of false -> digraph:add_vertex(G, Vertex, Label); _ -> ok = throw({graph_error, {vertex, duplicate, Vertex}}) - end || {Module, Atts} <- Graph, - {Vertex, Label} <- VertexFun(Module, Atts)], + end || GraphElem <- Graph, + {Vertex, Label} <- VertexFun(GraphElem)], [case digraph:add_edge(G, From, To) of {error, E} -> throw({graph_error, {edge, E, From, To}}); _ -> ok - end || {Module, Atts} <- Graph, - {From, To} <- EdgeFun(Module, Atts)], + end || GraphElem <- Graph, + {From, To} <- EdgeFun(GraphElem)], {ok, G} catch {graph_error, Reason} -> true = digraph:delete(G), diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index d3d76be0..edb7b12f 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -105,8 +105,8 @@ read_enabled(PluginsFile) -> %% the resulting list, otherwise they're skipped. dependencies(Reverse, Sources, AllPlugins) -> {ok, G} = rabbit_misc:build_acyclic_graph( - fun (App, _Deps) -> [{App, App}] end, - fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, + fun ({App, _Deps}) -> [{App, App}] end, + fun ({App, Deps}) -> [{App, Dep} || Dep <- Deps] end, lists:ukeysort( 1, [{Name, Deps} || #plugin{name = Name, diff --git a/src/rabbit_version.erl b/src/rabbit_version.erl index e4fcc596..ef480ccb 100644 --- a/src/rabbit_version.erl +++ b/src/rabbit_version.erl @@ -115,8 +115,8 @@ upgrades_required(Scope) -> with_upgrade_graph(Fun, Scope) -> Attrs = rabbit_misc:all_module_attributes(rabbit_upgrade), case rabbit_misc:build_acyclic_graph( - fun (Module, Steps) -> vertices(Module, Steps, Scope) end, - fun (Module, Steps) -> edges(Module, Steps, Scope) end, + fun ({Module, Steps}) -> vertices(Module, Steps, Scope) end, + fun ({Module, Steps}) -> edges(Module, Steps, Scope) end, [{Mod, Steps} || {_, Mod, Steps} <- Attrs]) of {ok, G} -> try Fun(G) -- cgit v1.2.1 From a75d518ae4b914a4bd9ef8f23d2cec8cf3ec6477 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Mar 2014 15:56:48 +0000 Subject: Oops, complete renaming that function. Also remove some exports that seem no longer used. --- src/rabbit.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 07b21e50..b4943a30 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -22,7 +22,6 @@ stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/1, start_fhc/0]). --export([run_boot_steps/0, load_steps/1, run_step/3]). -export([start/2, stop/1]). -export([start_apps/1, stop_apps/1]). -export([log_location/1]). %% for testing @@ -412,7 +411,7 @@ stop_apps(Apps) -> run_cleanup_steps(Apps) -> [run_step(Name, Attributes, cleanup) || - {App, Name, Attributes} <- load_steps(Apps), + {App, Name, Attributes} <- find_steps(Apps), lists:member(App, Apps)], ok. -- cgit v1.2.1 From ba7a814c8e7a2d2e4df1d3a46d593ef331a585ec Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Mar 2014 16:04:07 +0000 Subject: Remove -A and -I options, they are a bit too minority-interest. --- docs/rabbitmq-plugins.1.xml | 12 +----------- src/rabbit_plugins_main.erl | 27 ++++----------------------- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index f860e9c3..faa0bf41 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -69,7 +69,7 @@ - list -v -m -E -e -A -I pattern + list -v -m -E -e pattern @@ -90,16 +90,6 @@ Show only explicitly or implicitly enabled plugins. - - -A - Show active plugins (running on the broker) that - are not explicitly or implicitly enabled. - - - -I - Show in-active plugins (not running on the broker) that - are explicitly or implicitly enabled. - pattern Pattern to filter the plugin names by. diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 96fd83da..e7475a68 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -24,24 +24,19 @@ -define(MINIMAL_OPT, "-m"). -define(ENABLED_OPT, "-E"). -define(ENABLED_ALL_OPT, "-e"). --define(ACTIVE_ONLY_OPT, "-A"). --define(INACTIVE_ONLY_OPT, "-I"). -define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). -define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). -define(ENABLED_DEF, {?ENABLED_OPT, flag}). -define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). --define(ACTIVE_ONLY_DEF, {?ACTIVE_ONLY_OPT, flag}). --define(INACTIVE_ONLY_DEF, {?INACTIVE_ONLY_OPT, flag}). -define(RPC_TIMEOUT, infinity). -define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]). -define(COMMANDS, - [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, - ?ENABLED_ALL_DEF, ?ACTIVE_ONLY_DEF, ?INACTIVE_ONLY_DEF]}, + [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, enable, disable]). @@ -217,8 +212,6 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> end, OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), - OnlyActive = proplists:get_bool(?ACTIVE_ONLY_OPT, Opts), - OnlyInactive = proplists:get_bool(?INACTIVE_ONLY_OPT, Opts), AvailablePlugins = rabbit_plugins:list(PluginsDir), EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), @@ -237,17 +230,9 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> Plugins = [ Plugin || Plugin = #plugin{name = Name} <- AvailablePlugins ++ Missing, re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, - if OnlyEnabled -> lists:member(Name, EnabledExplicitly); - OnlyEnabledAll -> is_enabled(Name, EnabledExplicitly, - EnabledImplicitly); - OnlyActive -> (lists:member(Name, ActivePlugins) - andalso not - (is_enabled(Name, EnabledExplicitly, - EnabledImplicitly))); - OnlyInactive -> (is_enabled(Name, EnabledExplicitly, - EnabledImplicitly) - andalso not - lists:member(Name, ActivePlugins)); + if OnlyEnabled -> lists:member(Name, EnabledExplicitly); + OnlyEnabledAll -> lists:member(Name, EnabledExplicitly) or + lists:member(Name,EnabledImplicitly); true -> true end], Plugins1 = usort_plugins(Plugins), @@ -257,10 +242,6 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> plugin_names(Missing), Format, MaxWidth) || P <- Plugins1], ok. -is_enabled(Name, EnabledExplicitly, EnabledImplicitly) -> - lists:member(Name,EnabledExplicitly) or - lists:member(Name, EnabledImplicitly). - format_plugin(#plugin{name = Name, version = Version, description = Description, dependencies = Deps}, EnabledExplicitly, EnabledImplicitly, Active, -- cgit v1.2.1 From a41a85dfe4ccdaffaca0a96eadfa629408f8ec82 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Mar 2014 16:28:18 +0000 Subject: UI tweak: look more like the output of dpkg, with two separate characters to indicate desired / actual status. Also refer to a plugin as being "running" rather than "active" since "active" and "enabled" might get confused. --- docs/rabbitmq-plugins.1.xml | 18 +++++++--------- src/rabbit_plugins_main.erl | 52 ++++++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index faa0bf41..3b67d0e6 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -97,16 +97,14 @@ Lists all plugins, their versions, dependencies and - descriptions. Each plugin is prefixed with a status - indicator - [ ] to indicate that the plugin is not - enabled, [E] to indicate that it is explicitly enabled, - [e] to indicate that it is implicitly enabled, and [!] to - indicate that it is enabled but missing and thus not - operational. When a plugin is enabled but not currently - active (i.e., running) in the currently targetted broker, - [A] is shown, whilst a plugin which is active/running but - not currently configured to be enabled is indicated by - an [I]. + descriptions. Each plugin is prefixed with two status + indicator characters inside [ ]. The first indicator can + be " " to indicate that the plugin is not enabled, "E" to + indicate that it is explicitly enabled, "e" to indicate + that it is implicitly enabled, or "!" to indicate that it + is enabled but missing and thus not operational. The + second indicator can be " " to show that the plugin is not + running, or "*" to show that it is. If the optional pattern is given, only plugins whose diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index e7475a68..41d70724 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -218,11 +218,11 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> AllEnabled = rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins), EnabledImplicitly = AllEnabled -- EnabledExplicitly, - ActivePlugins = case rpc:call(Node, rabbit_plugins, active, - [], ?RPC_TIMEOUT) of - {badrpc, _} -> AllEnabled; - Active -> Active - end, + Running = case rpc:call(Node, rabbit_plugins, active, + [], ?RPC_TIMEOUT) of + {badrpc, _} -> AllEnabled; + Active -> Active + end, Missing = [#plugin{name = Name, dependencies = []} || Name <- ((EnabledExplicitly ++ EnabledImplicitly) -- plugin_names(AvailablePlugins))], @@ -238,26 +238,34 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> Plugins1 = usort_plugins(Plugins), MaxWidth = lists:max([length(atom_to_list(Name)) || #plugin{name = Name} <- Plugins1] ++ [0]), - [format_plugin(P, EnabledExplicitly, EnabledImplicitly, ActivePlugins, + case Format of + minimal -> ok; + _ -> io:format(" Configured: E = explicitly enabled; " + "e = implicitly enabled; ! = missing~n" + " | Status: * = running on ~s~n" + " |/~n", [Node]) + end, + [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Running, plugin_names(Missing), Format, MaxWidth) || P <- Plugins1], ok. format_plugin(#plugin{name = Name, version = Version, description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Active, + EnabledExplicitly, EnabledImplicitly, Running, Missing, Format, MaxWidth) -> - Glyph = case {lists:member(Name, EnabledExplicitly), - lists:member(Name, EnabledImplicitly), - lists:member(Name, Missing), - lists:member(Name, Active)} of - {true, false, false, false} -> "[I]"; - {false, true, false, false} -> "[i]"; - {false, false, false, true} -> "[A]"; - {true, false, false, true} -> "[E]"; - {false, true, false, true} -> "[e]"; - {_, _, true, _} -> "[!]"; - _ -> "[ ]" - end, + EnabledGlyph = case {lists:member(Name, EnabledExplicitly), + lists:member(Name, EnabledImplicitly), + lists:member(Name, Missing)} of + {true, false, false} -> "E"; + {false, true, false} -> "e"; + {_, _, true} -> "!"; + _ -> " " + end, + RunningGlyph = case lists:member(Name, Running) of + true -> "*"; + false -> " " + end, + Glyph = rabbit_misc:format("[~s~s]", [EnabledGlyph, RunningGlyph]), Opt = fun (_F, A, A) -> ok; ( F, A, _) -> io:format(F, [A]) end, @@ -268,9 +276,9 @@ format_plugin(#plugin{name = Name, version = Version, Opt("~s", Version, undefined), io:format("~n"); verbose -> io:format("~s ~w~n", [Glyph, Name]), - Opt(" Version: \t~s~n", Version, undefined), - Opt(" Dependencies:\t~p~n", Deps, []), - Opt(" Description: \t~s~n", Description, undefined), + Opt(" Version: \t~s~n", Version, undefined), + Opt(" Dependencies:\t~p~n", Deps, []), + Opt(" Description: \t~s~n", Description, undefined), io:format("~n") end. -- cgit v1.2.1 From f4aea50da2974c3d90cf6c82c9ecc3da87df50aa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Mar 2014 16:44:43 +0000 Subject: Tidy up the progress output slightly. --- src/rabbit_plugins_main.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 41d70724..33f69732 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -331,17 +331,17 @@ rpc_call(Node, Mod, Action, Args) -> end. rpc_progress(Key, Node, Action) -> - case rpc:nb_yield(Key, 100) of + case rpc:nb_yield(Key, 1000) of timeout -> io:format("."), rpc_progress(Key, Node, Action); {value, {badrpc, nodedown}} -> - io:format(". error: Unable to contact ~p.~n ", [Node]), + io:format(". error.~nUnable to contact ~p.~n ", [Node]), io:format("Please start the broker to apply " "your changes.~n"); {value, ok} -> - io:format(". done: Plugin(s) ~pd.~n", [Action]); + io:format(". done.~n", []); {value, Error} -> - io:format(". error: Unable to ~p plugin(s).~n" + io:format(". error.~nUnable to ~p plugin(s).~n" "Please restart the broker to apply your changes.~n" "Error: ~p~n", [Action, Error]) -- cgit v1.2.1 From 5480ea3d5300354f6a9c6e401266a0118bdf1575 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 26 Mar 2014 11:51:25 +0000 Subject: Crash when node is down and --offline is not given --- src/rabbit_plugins_main.erl | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 33f69732..42762e92 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -18,25 +18,29 @@ -include("rabbit.hrl"). -export([start/0, stop/0]). +-export([action/6]). -define(NODE_OPT, "-n"). -define(VERBOSE_OPT, "-v"). -define(MINIMAL_OPT, "-m"). -define(ENABLED_OPT, "-E"). -define(ENABLED_ALL_OPT, "-e"). +-define(OFFLINE_OPT, "--offline"). -define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). -define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). -define(ENABLED_DEF, {?ENABLED_OPT, flag}). -define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). +-define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). -define(RPC_TIMEOUT, infinity). -define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]). -define(COMMANDS, - [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, + [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF, + ?OFFLINE_DEF]}, enable, disable]). @@ -114,7 +118,7 @@ action(list, Node, [], Opts, PluginsFile, PluginsDir) -> action(list, Node, [Pat], Opts, PluginsFile, PluginsDir) -> format_plugins(Node, Pat, Opts, PluginsFile, PluginsDir); -action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> +action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> case ToEnable0 of [] -> throw({error_string, "Not enough arguments for 'enable'"}); _ -> ok @@ -128,7 +132,7 @@ action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> ExplicitlyEnabled = lists:usort(Enabled ++ ToEnable), NewEnabled = case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> ExplicitlyEnabled; + {badrpc, _} -> ensure_offline(Opts, Node, ExplicitlyEnabled); [] -> ExplicitlyEnabled; ActiveList -> EnabledSet = sets:from_list(ExplicitlyEnabled), ActiveSet = sets:from_list(ActiveList), @@ -154,7 +158,7 @@ action(enable, Node, ToEnable0, _Opts, PluginsFile, PluginsDir) -> action_change(Node, enable, NewEnabled) end; -action(disable, Node, ToDisable0, _Opts, PluginsFile, PluginsDir) -> +action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> case ToDisable0 of [] -> throw({error_string, "Not enough arguments for 'disable'"}); _ -> ok @@ -171,7 +175,7 @@ action(disable, Node, ToDisable0, _Opts, PluginsFile, PluginsDir) -> ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins), Active = case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> Enabled; + {badrpc, _} -> ensure_offline(Opts, Node, Enabled); [] -> Enabled; ActiveList -> ActiveList end, @@ -192,6 +196,15 @@ action(disable, Node, ToDisable0, _Opts, PluginsFile, PluginsDir) -> %%---------------------------------------------------------------------------- +ensure_offline(Opts, Node, Thing) -> + case proplists:get_bool(?OFFLINE_OPT, Opts) of + true -> Thing; + false -> Msg = io_lib:format("Unable to contact node: ~p - " + "To make your changes anyway, " + "try again with --offline~n", [Node]), + throw({error_string, Msg}) + end. + print_error(Format, Args) -> rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). -- cgit v1.2.1 From 49a5a1efe3237e280d80827673e6534670900758 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 26 Mar 2014 12:05:38 +0000 Subject: Oops - set up options/commands properly --- src/rabbit_plugins_main.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 42762e92..45df6e51 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -39,10 +39,9 @@ -define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]). -define(COMMANDS, - [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF, - ?OFFLINE_DEF]}, - enable, - disable]). + [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, + {enable, [?OFFLINE_DEF]}, + {disable, [?OFFLINE_DEF]}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From a994cd8db832684b837ede757d7b2052a9593e57 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 28 Mar 2014 13:46:10 +0000 Subject: Ensure start_app considers plugins when using RABBITMQ_NODE_ONLY - always start all enabled plugins, regardless of startup type - only disable plugins that are currently running The latter incidentally fixes bug 24941. --- src/rabbit.erl | 14 ++++++++------ src/rabbit_plugins.erl | 7 +++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index b4943a30..6c08520f 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -311,8 +311,7 @@ start() -> ok = ensure_working_log_handlers(), rabbit_node_monitor:prepare_cluster_status_files(), rabbit_mnesia:check_cluster_consistency(), - start_apps(app_startup_order()), - ok = log_broker_started(rabbit_plugins:active()) + broker_start() end). boot() -> @@ -327,12 +326,15 @@ boot() -> %% the upgrade, since if we are a secondary node the %% primary node will have forgotten us rabbit_mnesia:check_cluster_consistency(), - Plugins = rabbit_plugins:setup(), - ToBeLoaded = Plugins ++ ?APPS, - start_apps(ToBeLoaded), - ok = log_broker_started(Plugins) + broker_start() end). +broker_start() -> + Plugins = rabbit_plugins:setup(), + ToBeLoaded = Plugins ++ ?APPS, + start_apps(ToBeLoaded), + ok = log_broker_started(rabbit_plugins:active()). + handle_app_error(Term) -> fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> throw({Term, App, ExitReason}); diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index edb7b12f..cc65c569 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -44,8 +44,11 @@ enable(Plugins) -> ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]). disable(Plugins) -> - Diff = rabbit:stop_apps(Plugins), - ok = rabbit_event:notify(plugins_changed, [{disabled, Plugins}]). + RunningApps = rabbit_misc:which_applications(), + ToDisable = [P || P <- Plugins, + proplists:is_defined(P, RunningApps)], + rabbit:stop_apps(ToDisable), + ok = rabbit_event:notify(plugins_changed, [{disabled, ToDisable}]). %% @doc Prepares the file system and installs all enabled plugins. setup() -> -- cgit v1.2.1 From 28eb8a62293a26c3cdbc142503a1cd3de6e439c8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 28 Mar 2014 16:04:00 +0000 Subject: Keep online and offline plugin changes separate in plugins_main --- src/rabbit_plugins_main.erl | 74 ++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 45df6e51..7209d735 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -129,14 +129,20 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), ExplicitlyEnabled = lists:usort(Enabled ++ ToEnable), + OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts), NewEnabled = - case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> ensure_offline(Opts, Node, ExplicitlyEnabled); - [] -> ExplicitlyEnabled; - ActiveList -> EnabledSet = sets:from_list(ExplicitlyEnabled), - ActiveSet = sets:from_list(ActiveList), - Intersect = sets:intersection(EnabledSet, ActiveSet), - sets:to_list(sets:subtract(EnabledSet, Intersect)) + case OfflineOnly of + true -> ExplicitlyEnabled; + false -> + case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of + {badrpc, _} -> rpc_failure(Node); + [] -> ExplicitlyEnabled; + ActiveList -> + EnabledSet = sets:from_list(ExplicitlyEnabled), + ActiveSet = sets:from_list(ActiveList), + Intersect = sets:intersection(EnabledSet, ActiveSet), + sets:to_list(sets:subtract(EnabledSet, Intersect)) + end end, NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), @@ -154,7 +160,7 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> [] -> io:format("Plugin configuration unchanged.~n"); _ -> print_list("The following plugins have been enabled:", NewEnabled), - action_change(Node, enable, NewEnabled) + action_change(OfflineOnly, Node, enable, NewEnabled) end; action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> @@ -172,11 +178,16 @@ action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> Missing) end, ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins), + OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts), Active = - case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> ensure_offline(Opts, Node, Enabled); - [] -> Enabled; - ActiveList -> ActiveList + case OfflineOnly of + true -> Enabled; + false -> case rpc:call(Node, rabbit_plugins, active, + [], ?RPC_TIMEOUT) of + {badrpc, _} -> rpc_failure(Node); + [] -> Enabled; + ActiveList -> ActiveList + end end, NewEnabled = Enabled -- ToDisableDeps, case length(Active) =:= length(NewEnabled) of @@ -190,19 +201,16 @@ action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> print_list("The following plugins have been disabled:", Disabled), write_enabled_plugins(PluginsFile, NewEnabled), - action_change(Node, disable, Disabled) + action_change(OfflineOnly, Node, disable, Disabled) end. %%---------------------------------------------------------------------------- -ensure_offline(Opts, Node, Thing) -> - case proplists:get_bool(?OFFLINE_OPT, Opts) of - true -> Thing; - false -> Msg = io_lib:format("Unable to contact node: ~p - " +rpc_failure(Node) -> + Msg = io_lib:format("Unable to contact node: ~p - " "To make your changes anyway, " "try again with --offline~n", [Node]), - throw({error_string, Msg}) - end. + throw({error_string, Msg}). print_error(Format, Args) -> rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). @@ -323,24 +331,22 @@ write_enabled_plugins(PluginsFile, Plugins) -> PluginsFile, Reason}}) end. -action_change(Node, Action, Targets) -> +action_change(true, _Node, Action, _Targets) -> + io:format("Plugin configuration has changed. " + "Plugins were not ~p since the node is down.~n" + "Please start the broker to apply " + "your changes.~n", + [case Action of + enable -> started; + disable -> stopped + end]); +action_change(false, Node, Action, Targets) -> rpc_call(Node, rabbit_plugins, Action, [Targets]). rpc_call(Node, Mod, Action, Args) -> - case net_adm:ping(Node) of - pong -> io:format("Changing plugin configuration on ~p.", [Node]), - AsyncKey = rpc:async_call(Node, Mod, Action, Args), - rpc_progress(AsyncKey, Node, Action); - pang -> io:format("Plugin configuration has changed. " - "Plugins were not ~p since the node is down.~n" - "Please start the broker to apply " - "your changes.~n", - [case Action of - enable -> started; - disable -> stopped - end]) - - end. + io:format("Changing plugin configuration on ~p.", [Node]), + AsyncKey = rpc:async_call(Node, Mod, Action, Args), + rpc_progress(AsyncKey, Node, Action). rpc_progress(Key, Node, Action) -> case rpc:nb_yield(Key, 1000) of -- cgit v1.2.1 From c2ad129d241492576f039419e83dcc53ed4f0f5c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 28 Mar 2014 16:14:58 +0000 Subject: Calculate the enabled set properly and tweak the UI --- src/rabbit_plugins_main.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 7209d735..b9fdfcd0 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -132,7 +132,7 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts), NewEnabled = case OfflineOnly of - true -> ExplicitlyEnabled; + true -> ToEnable -- Enabled; false -> case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of {badrpc, _} -> rpc_failure(Node); @@ -332,10 +332,8 @@ write_enabled_plugins(PluginsFile, Plugins) -> end. action_change(true, _Node, Action, _Targets) -> - io:format("Plugin configuration has changed. " - "Plugins were not ~p since the node is down.~n" - "Please start the broker to apply " - "your changes.~n", + io:format("Offline Mode: No plugins were ~p.~n" + "Please (re)start the broker to apply your changes.~n", [case Action of enable -> started; disable -> stopped -- cgit v1.2.1 From 021819d55ad25b563bdb75ade2c796a756ea4ebb Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 8 Apr 2014 13:55:36 +0100 Subject: Fix management extension enable/disable handling When notifying about 'enabled' plugins, limit to explicitly given. When handling 'disabled' plugins, call the event handler(s) synchronously (while we still have loaded modules) prior to stopping and unloading any apps. --- src/rabbit.erl | 6 ++++-- src/rabbit_event.erl | 24 ++++++++++++++++++++---- src/rabbit_plugins.erl | 8 ++++---- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 6c08520f..8a682616 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -210,6 +210,7 @@ %% this really should be an abstract type -type(log_location() :: 'tty' | 'undefined' | file:filename()). -type(param() :: atom()). +-type(app_name() :: atom()). -spec(start/0 :: () -> 'ok'). -spec(boot/0 :: () -> 'ok'). @@ -241,6 +242,8 @@ -spec(maybe_insert_default_data/0 :: () -> 'ok'). -spec(boot_delegate/0 :: () -> 'ok'). -spec(recover/0 :: () -> 'ok'). +-spec(start_apps/1 :: ([app_name()]) -> 'ok'). +-spec(stop_apps/1 :: ([app_name()]) -> 'ok'). -endif. @@ -350,8 +353,7 @@ start_apps(Apps) -> _ -> ok end, ok = app_utils:start_applications(StartupApps, - handle_app_error(could_not_start)), - StartupApps. + handle_app_error(could_not_start)). start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index b867223b..88726900 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -23,6 +23,7 @@ ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). -export([stats_level/2, if_enabled/3]). -export([notify/2, notify/3, notify_if/3]). +-export([sync_notify/2, sync_notify/3]). %%---------------------------------------------------------------------------- @@ -61,6 +62,9 @@ -spec(notify/2 :: (event_type(), event_props()) -> 'ok'). -spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok'). -spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok'). +-spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok'). +-spec(sync_notify/3 :: (event_type(), event_props(), + reference() | 'none') -> 'ok'). -endif. @@ -145,7 +149,19 @@ notify_if(false, _Type, _Props) -> ok. notify(Type, Props) -> notify(Type, Props, none). notify(Type, Props, Ref) -> - gen_event:notify(?MODULE, #event{type = Type, - props = Props, - reference = Ref, - timestamp = os:timestamp()}). + do_notify(notify, #event{type = Type, + props = Props, + reference = Ref, + timestamp = os:timestamp()}). + +sync_notify(Type, Props) -> sync_notify(Type, Props, none). + +sync_notify(Type, Props, Ref) -> + do_notify(sync_notify, #event{ type = Type, + props = Props, + reference = Ref, + timestamp = os:timestamp()}). + +do_notify(F, Event) -> + apply(gen_event, F, [?MODULE, Event]). + diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index cc65c569..767e4adc 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -40,15 +40,15 @@ enable(Plugins) -> prepare_plugins(Plugins), - Diff = rabbit:start_apps(Plugins), - ok = rabbit_event:notify(plugins_changed, [{enabled, Diff}]). + rabbit:start_apps(Plugins), + ok = rabbit_event:notify(plugins_changed, [{enabled, Plugins}]). disable(Plugins) -> RunningApps = rabbit_misc:which_applications(), ToDisable = [P || P <- Plugins, proplists:is_defined(P, RunningApps)], - rabbit:stop_apps(ToDisable), - ok = rabbit_event:notify(plugins_changed, [{disabled, ToDisable}]). + ok = rabbit_event:sync_notify(plugins_changed, [{disabled, ToDisable}]), + rabbit:stop_apps(ToDisable). %% @doc Prepares the file system and installs all enabled plugins. setup() -> -- cgit v1.2.1 From 7aa1f652e26573064442faefa8a96922b3c1215e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 9 Apr 2014 16:14:17 +0100 Subject: Explain --- src/rabbit_dead_letter.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_dead_letter.erl b/src/rabbit_dead_letter.erl index ec32e687..728bc431 100644 --- a/src/rabbit_dead_letter.erl +++ b/src/rabbit_dead_letter.erl @@ -129,6 +129,9 @@ is_cycle(Queue, Deaths) -> {longstr, <<"rejected">>} =/= rabbit_misc:table_lookup(D, <<"reason">>); (_) -> + %% There was something we didn't expect, therefore + %% a client must have put it there, therefore the + %% cycle was not "fully automatic". false end, Cycle ++ [H]) end. -- cgit v1.2.1 From 7e6869cfec8dcd486c4d9b5f5b949874df2e8c02 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 10 Apr 2014 22:10:18 +0100 Subject: Cosmetic (consistency) --- src/rabbit_event.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 88726900..d3cd1a63 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -157,10 +157,10 @@ notify(Type, Props, Ref) -> sync_notify(Type, Props) -> sync_notify(Type, Props, none). sync_notify(Type, Props, Ref) -> - do_notify(sync_notify, #event{ type = Type, - props = Props, - reference = Ref, - timestamp = os:timestamp()}). + do_notify(sync_notify, #event{type = Type, + props = Props, + reference = Ref, + timestamp = os:timestamp()}). do_notify(F, Event) -> apply(gen_event, F, [?MODULE, Event]). -- cgit v1.2.1 From 8fc94c42d3cf8b6da16649875efafe54f1272ec3 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 Apr 2014 12:26:40 +0100 Subject: Better RPC diagnostics when the node is down --- src/rabbit_plugins_main.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index b9fdfcd0..3b9d30f2 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -207,9 +207,11 @@ action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> %%---------------------------------------------------------------------------- rpc_failure(Node) -> - Msg = io_lib:format("Unable to contact node: ~p - " - "To make your changes anyway, " - "try again with --offline~n", [Node]), + RpcMsg = rabbit_nodes:diagnostics([Node]), + Msg = io_lib:format("Unable to contact ~p~n" + "To apply these changes anyway, " + "try again with --offline~n" + "~s", [Node, RpcMsg]), throw({error_string, Msg}). print_error(Format, Args) -> -- cgit v1.2.1 From 942f5c4284039338edb73a24fe878e0e3f9ecdff Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Mon, 21 Apr 2014 09:49:01 +0200 Subject: internal_error -> precondition_failed --- src/rabbit_channel_interceptor.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel_interceptor.erl b/src/rabbit_channel_interceptor.erl index 81c17fbf..1b6178c1 100644 --- a/src/rabbit_channel_interceptor.erl +++ b/src/rabbit_channel_interceptor.erl @@ -68,17 +68,17 @@ intercept_method(M, VHost, [I]) -> true -> M2; _ -> - internal_error("Interceptor: ~p expected " + precondition_failed("Interceptor: ~p expected " "to return method: ~p but returned: ~p", [I, rabbit_misc:method_record_type(M), rabbit_misc:method_record_type(M2)]) end; {error, Reason} -> - internal_error("Interceptor: ~p failed with reason: ~p", + precondition_failed("Interceptor: ~p failed with reason: ~p", [I, Reason]) end; intercept_method(M, _VHost, Is) -> - internal_error("More than one interceptor for method: ~p -- ~p", + precondition_failed("More than one interceptor for method: ~p -- ~p", [rabbit_misc:method_record_type(M), Is]). %% select the interceptors that apply to intercept_method(). @@ -91,6 +91,6 @@ validate_method(M, M2) -> rabbit_misc:method_record_type(M) =:= rabbit_misc:method_record_type(M2). %% keep dialyzer happy --spec internal_error(string(), [any()]) -> no_return(). -internal_error(Format, Args) -> - rabbit_misc:protocol_error(internal_error, Format, Args). +-spec precondition_failed(string(), [any()]) -> no_return(). +precondition_failed(Format, Args) -> + rabbit_misc:protocol_error(precondition_failed, Format, Args). -- cgit v1.2.1 From 68366c7f6a7a27901a9ae07d5636f35bfaa65faf Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Mon, 21 Apr 2014 09:54:09 +0200 Subject: updates intercept callback type --- src/rabbit_channel_interceptor.erl | 2 +- src/rabbit_misc.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel_interceptor.erl b/src/rabbit_channel_interceptor.erl index 1b6178c1..0dbdf5ac 100644 --- a/src/rabbit_channel_interceptor.erl +++ b/src/rabbit_channel_interceptor.erl @@ -33,7 +33,7 @@ -callback description() -> [proplists:property()]. -callback intercept(original_method(), rabbit_types:vhost()) -> - rabbit_types:ok_or_error2(processed_method(), any()). + {ok, processed_method()} | rabbit_misc:channel_or_connection_exit(). %% Whether the interceptor wishes to intercept the amqp method -callback applies_to(intercept_method()) -> boolean(). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 58e93a3f..18c07f86 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -81,7 +81,7 @@ -ifdef(use_specs). --export_type([resource_name/0, thunk/1]). +-export_type([resource_name/0, thunk/1, channel_or_connection_exit/0]). -type(ok_or_error() :: rabbit_types:ok_or_error(any())). -type(thunk(T) :: fun(() -> T)). -- cgit v1.2.1 From 6444cdd4edad85721bd2f1f80e5fbfb2b2e8146e Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Tue, 29 Apr 2014 16:29:34 +0200 Subject: updates intercept callback signature --- src/rabbit_channel_interceptor.erl | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/rabbit_channel_interceptor.erl b/src/rabbit_channel_interceptor.erl index 0dbdf5ac..d27dc8b9 100644 --- a/src/rabbit_channel_interceptor.erl +++ b/src/rabbit_channel_interceptor.erl @@ -33,7 +33,7 @@ -callback description() -> [proplists:property()]. -callback intercept(original_method(), rabbit_types:vhost()) -> - {ok, processed_method()} | rabbit_misc:channel_or_connection_exit(). + processed_method() | rabbit_misc:channel_or_connection_exit(). %% Whether the interceptor wishes to intercept the amqp method -callback applies_to(intercept_method()) -> boolean(). @@ -62,20 +62,15 @@ intercept_method(M, VHost) -> intercept_method(M, _VHost, []) -> M; intercept_method(M, VHost, [I]) -> - case I:intercept(M, VHost) of - {ok, M2} -> - case validate_method(M, M2) of - true -> - M2; - _ -> - precondition_failed("Interceptor: ~p expected " - "to return method: ~p but returned: ~p", - [I, rabbit_misc:method_record_type(M), - rabbit_misc:method_record_type(M2)]) - end; - {error, Reason} -> - precondition_failed("Interceptor: ~p failed with reason: ~p", - [I, Reason]) + M2 = I:intercept(M, VHost), + case validate_method(M, M2) of + true -> + M2; + _ -> + precondition_failed("Interceptor: ~p expected " + "to return method: ~p but returned: ~p", + [I, rabbit_misc:method_record_type(M), + rabbit_misc:method_record_type(M2)]) end; intercept_method(M, _VHost, Is) -> precondition_failed("More than one interceptor for method: ~p -- ~p", -- cgit v1.2.1 From 0037c0eebee5b8fe1090041d6c33f752bd25ac7b Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Tue, 29 Apr 2014 16:38:25 +0200 Subject: internal_error -> precondition_failed --- src/rabbit_channel_interceptor.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel_interceptor.erl b/src/rabbit_channel_interceptor.erl index d27dc8b9..db9349ac 100644 --- a/src/rabbit_channel_interceptor.erl +++ b/src/rabbit_channel_interceptor.erl @@ -67,13 +67,13 @@ intercept_method(M, VHost, [I]) -> true -> M2; _ -> - precondition_failed("Interceptor: ~p expected " + internal_error("Interceptor: ~p expected " "to return method: ~p but returned: ~p", [I, rabbit_misc:method_record_type(M), rabbit_misc:method_record_type(M2)]) end; intercept_method(M, _VHost, Is) -> - precondition_failed("More than one interceptor for method: ~p -- ~p", + internal_error("More than one interceptor for method: ~p -- ~p", [rabbit_misc:method_record_type(M), Is]). %% select the interceptors that apply to intercept_method(). @@ -86,6 +86,6 @@ validate_method(M, M2) -> rabbit_misc:method_record_type(M) =:= rabbit_misc:method_record_type(M2). %% keep dialyzer happy --spec precondition_failed(string(), [any()]) -> no_return(). -precondition_failed(Format, Args) -> - rabbit_misc:protocol_error(precondition_failed, Format, Args). +-spec internal_error(string(), [any()]) -> no_return(). +internal_error(Format, Args) -> + rabbit_misc:protocol_error(internal_error, Format, Args). -- cgit v1.2.1 From c242e129e53f9964628915b713caee9fbf73ff2a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 May 2014 16:50:31 +0100 Subject: Stop the whole queue if the alternative is to fail over to an unsynced slave. --- src/rabbit_mirror_queue_coordinator.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 23 +++++++++++++++++------ src/rabbit_mirror_queue_slave.erl | 9 ++++++--- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 23718da1..6a67a759 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -368,7 +368,7 @@ handle_cast(request_depth, State = #state { depth_fun = DepthFun }) -> handle_cast({ensure_monitoring, Pids}, State = #state { monitors = Mons }) -> noreply(State #state { monitors = pmon:monitor_all(Pids, Mons) }); -handle_cast({delete_and_terminate, Reason}, State) -> +handle_cast({terminate, _Delete, Reason}, State) -> {stop, Reason, State}. handle_info({'DOWN', _MonitorRef, process, Pid, _Reason}, @@ -410,7 +410,7 @@ handle_msg([CPid], _From, request_depth = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); -handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> +handle_msg([CPid], _From, {terminate, _Delete, _Reason} = Msg) -> ok = gen_server2:cast(CPid, Msg), {stop, {shutdown, ring_shutdown}}; handle_msg([_CPid], _From, _Msg) -> diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 2b16b911..c6e5a7d8 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -130,7 +130,8 @@ stop_mirroring(State = #state { coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS }) -> unlink(CPid), - stop_all_slaves(shutdown, State), + %% delete = *slaves* should delete + stop_all_slaves(shutdown, delete, State), {BQ, BQS}. sync_mirrors(HandleInfo, EmitStats, @@ -170,21 +171,31 @@ terminate({shutdown, dropped} = Reason, State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}; terminate(Reason, - State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> + State = #state { name = QName, + backing_queue = BQ, + backing_queue_state = BQS }) -> %% Backing queue termination. The queue is going down but %% shouldn't be deleted. Most likely safe shutdown of this - %% node. Thus just let some other slave take over. + %% node. + {ok, #amqqueue{sync_slave_pids = SSPids}} = rabbit_amqqueue:lookup(QName), + %% TODO there should be a policy for this + case SSPids of + [] -> %% Remove the whole queue to avoid data loss + stop_all_slaves(Reason, nodelete, State); + _ -> %% Just let some other slave take over. + ok + end, State #state { backing_queue_state = BQ:terminate(Reason, BQS) }. delete_and_terminate(Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - stop_all_slaves(Reason, State), + stop_all_slaves(Reason, delete, State), State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}. -stop_all_slaves(Reason, #state{name = QName, gm = GM}) -> +stop_all_slaves(Reason, Delete, #state{name = QName, gm = GM}) -> {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), MRefs = [erlang:monitor(process, Pid) || Pid <- [GM | SPids]], - ok = gm:broadcast(GM, {delete_and_terminate, Reason}), + ok = gm:broadcast(GM, {terminate, Delete, Reason}), [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], %% Normally when we remove a slave another slave or master will %% notice and update Mnesia. But we just removed them all, and diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ee889f84..e30b941d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -420,7 +420,7 @@ handle_msg([_SPid], _From, process_death) -> %% messages from the master we have yet to receive. When we get %% members_changed, then there will be no more messages. ok; -handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> +handle_msg([CPid], _From, {terminate, _Delete, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; handle_msg([SPid], _From, {sync_start, Ref, Syncer, SPids}) -> @@ -876,10 +876,13 @@ process_instruction({depth, Depth}, backing_queue_state = BQS }) -> {ok, set_delta(Depth - BQ:depth(BQS), State)}; -process_instruction({delete_and_terminate, Reason}, +process_instruction({terminate, Delete, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - BQ:delete_and_terminate(Reason, BQS), + case Delete of + delete -> BQ:delete_and_terminate(Reason, BQS); + nodelete -> BQ:terminate(Reason, BQS) + end, {stop, State #state { backing_queue_state = undefined }}. msg_ids_to_acktags(MsgIds, MA) -> -- cgit v1.2.1 From 16056f2702cc1d4a251148e4bfaf2f154c13e07b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 May 2014 14:56:26 +0100 Subject: Actually, slaves should always delete_and_terminate; when shutting them donw in this case we still don't trust their contents. --- src/rabbit_mirror_queue_coordinator.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 9 ++++----- src/rabbit_mirror_queue_slave.erl | 9 +++------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 6a67a759..74c03e64 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -368,7 +368,7 @@ handle_cast(request_depth, State = #state { depth_fun = DepthFun }) -> handle_cast({ensure_monitoring, Pids}, State = #state { monitors = Mons }) -> noreply(State #state { monitors = pmon:monitor_all(Pids, Mons) }); -handle_cast({terminate, _Delete, Reason}, State) -> +handle_cast({delete_and_terminate, Reason}, State) -> {stop, Reason, State}. handle_info({'DOWN', _MonitorRef, process, Pid, _Reason}, @@ -410,7 +410,7 @@ handle_msg([CPid], _From, request_depth = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); -handle_msg([CPid], _From, {terminate, _Delete, _Reason} = Msg) -> +handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, Msg), {stop, {shutdown, ring_shutdown}}; handle_msg([_CPid], _From, _Msg) -> diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c6e5a7d8..e8819bfa 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -130,8 +130,7 @@ stop_mirroring(State = #state { coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS }) -> unlink(CPid), - %% delete = *slaves* should delete - stop_all_slaves(shutdown, delete, State), + stop_all_slaves(shutdown, State), {BQ, BQS}. sync_mirrors(HandleInfo, EmitStats, @@ -189,13 +188,13 @@ terminate(Reason, delete_and_terminate(Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - stop_all_slaves(Reason, delete, State), + stop_all_slaves(Reason, State), State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}. -stop_all_slaves(Reason, Delete, #state{name = QName, gm = GM}) -> +stop_all_slaves(Reason, #state{name = QName, gm = GM}) -> {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), MRefs = [erlang:monitor(process, Pid) || Pid <- [GM | SPids]], - ok = gm:broadcast(GM, {terminate, Delete, Reason}), + ok = gm:broadcast(GM, {delete_and_terminate, Reason}), [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], %% Normally when we remove a slave another slave or master will %% notice and update Mnesia. But we just removed them all, and diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index e30b941d..ee889f84 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -420,7 +420,7 @@ handle_msg([_SPid], _From, process_death) -> %% messages from the master we have yet to receive. When we get %% members_changed, then there will be no more messages. ok; -handle_msg([CPid], _From, {terminate, _Delete, _Reason} = Msg) -> +handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; handle_msg([SPid], _From, {sync_start, Ref, Syncer, SPids}) -> @@ -876,13 +876,10 @@ process_instruction({depth, Depth}, backing_queue_state = BQS }) -> {ok, set_delta(Depth - BQ:depth(BQS), State)}; -process_instruction({terminate, Delete, Reason}, +process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - case Delete of - delete -> BQ:delete_and_terminate(Reason, BQS); - nodelete -> BQ:terminate(Reason, BQS) - end, + BQ:delete_and_terminate(Reason, BQS), {stop, State #state { backing_queue_state = undefined }}. msg_ids_to_acktags(MsgIds, MA) -> -- cgit v1.2.1 From 6e1d671300c00a0ed5ed8f6b8f692aa85c8caf4e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 May 2014 14:56:49 +0100 Subject: Add a policy for this thing --- src/rabbit_mirror_queue_master.erl | 15 ++++++++------- src/rabbit_mirror_queue_misc.erl | 26 ++++++++++++++++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e8819bfa..792cebd1 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -176,13 +176,14 @@ terminate(Reason, %% Backing queue termination. The queue is going down but %% shouldn't be deleted. Most likely safe shutdown of this %% node. - {ok, #amqqueue{sync_slave_pids = SSPids}} = rabbit_amqqueue:lookup(QName), - %% TODO there should be a policy for this - case SSPids of - [] -> %% Remove the whole queue to avoid data loss - stop_all_slaves(Reason, nodelete, State); - _ -> %% Just let some other slave take over. - ok + {ok, Q = #amqqueue{sync_slave_pids = SSPids}} = + rabbit_amqqueue:lookup(QName), + case SSPids =:= [] andalso + rabbit_policy:get(<<"ha-promote-on-shutdown">>, Q) =/= <<"always">> of + true -> %% Remove the whole queue to avoid data loss + stop_all_slaves(Reason, State); + false -> %% Just let some other slave take over. + ok end, State #state { backing_queue_state = BQ:terminate(Reason, BQS) }. diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index b0f092a9..8ba6968c 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -374,16 +374,21 @@ validate_policy(KeyList) -> Mode = proplists:get_value(<<"ha-mode">>, KeyList, none), Params = proplists:get_value(<<"ha-params">>, KeyList, none), SyncMode = proplists:get_value(<<"ha-sync-mode">>, KeyList, none), - case {Mode, Params, SyncMode} of - {none, none, none} -> + PromoteOnShutdown = proplists:get_value( + <<"ha-promote-on-shutdown">>, KeyList, none), + case {Mode, Params, SyncMode, PromoteOnShutdown} of + {none, none, none, none} -> ok; - {none, _, _} -> - {error, "ha-mode must be specified to specify ha-params or " - "ha-sync-mode", []}; + {none, _, _, _} -> + {error, "ha-mode must be specified to specify ha-params, " + "ha-sync-mode or ha-promote-on-shutdown", []}; _ -> case module(Mode) of {ok, M} -> case M:validate_policy(Params) of - ok -> validate_sync_mode(SyncMode); + ok -> case validate_sync_mode(SyncMode) of + ok -> validate_pos(PromoteOnShutdown); + E -> E + end; E -> E end; _ -> {error, "~p is not a valid ha-mode value", [Mode]} @@ -398,3 +403,12 @@ validate_sync_mode(SyncMode) -> Mode -> {error, "ha-sync-mode must be \"manual\" " "or \"automatic\", got ~p", [Mode]} end. + +validate_pos(PromoteOnShutdown) -> + case PromoteOnShutdown of + <<"always">> -> ok; + <<"when-synced">> -> ok; + none -> ok; + Mode -> {error, "ha-promote-on-shutdown must be " + "\"always\" or \"when-synced\", got ~p", [Mode]} + end. -- cgit v1.2.1 From bfa27a8bbc72dd0c2b5b414c6dad401bcd8638f8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 May 2014 11:03:51 +0100 Subject: Add boot step! --- src/rabbit_mirror_queue_misc.erl | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8ba6968c..7aec1ac8 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -29,16 +29,19 @@ -include("rabbit.hrl"). --rabbit_boot_step({?MODULE, - [{description, "HA policy validation"}, - {mfa, {rabbit_registry, register, - [policy_validator, <<"ha-mode">>, ?MODULE]}}, - {mfa, {rabbit_registry, register, - [policy_validator, <<"ha-params">>, ?MODULE]}}, - {mfa, {rabbit_registry, register, - [policy_validator, <<"ha-sync-mode">>, ?MODULE]}}, - {requires, rabbit_registry}, - {enables, recovery}]}). +-rabbit_boot_step( + {?MODULE, + [{description, "HA policy validation"}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-mode">>, ?MODULE]}}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-params">>, ?MODULE]}}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-sync-mode">>, ?MODULE]}}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-promote-on-shutdown">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, recovery}]}). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 259b8cef38e4c27d9b93c5a6ff9c42f172e5e9c0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 May 2014 12:45:19 +0100 Subject: Cosmetic, and log a warning --- src/rabbit_mirror_queue_coordinator.erl | 2 +- src/rabbit_mirror_queue_master.erl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 74c03e64..23718da1 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -410,7 +410,7 @@ handle_msg([CPid], _From, request_depth = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); -handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> +handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, Msg), {stop, {shutdown, ring_shutdown}}; handle_msg([_CPid], _From, _Msg) -> diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 792cebd1..24b22d4c 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -181,6 +181,9 @@ terminate(Reason, case SSPids =:= [] andalso rabbit_policy:get(<<"ha-promote-on-shutdown">>, Q) =/= <<"always">> of true -> %% Remove the whole queue to avoid data loss + rabbit_mirror_queue_misc:log_warning( + QName, "Stopping all nodes on master shutdown since no " + "synchronised slave is available~n", []), stop_all_slaves(Reason, State); false -> %% Just let some other slave take over. ok -- cgit v1.2.1 From 0e89c5bfaffb2ce89281e3515548a46e7f3f364e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 May 2014 13:58:08 +0100 Subject: Update example config --- docs/rabbitmq.config.example | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index b0e13b1b..26de71b7 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -257,9 +257,13 @@ %% {certfile, "/path/to/cert.pem"}, %% {keyfile, "/path/to/key.pem"}]}]}, + %% One of 'basic', 'detailed' or 'none'. See + %% http://www.rabbitmq.com/management.html#fine-stats for more details. + %% {rates_mode, basic}, + %% Configure how long aggregated data (such as message rates and queue %% lengths) is retained. Please read the plugin's documentation in - %% https://www.rabbitmq.com/management.html#configuration for more + %% http://www.rabbitmq.com/management.html#configuration for more %% details. %% %% {sample_retention_policies, @@ -268,14 +272,6 @@ %% {detailed, [{10, 5}]}]} ]}, - {rabbitmq_management_agent, - [%% Misc/Advanced Options - %% - %% NB: Change these only if you understand what you are doing! - %% - %% {force_fine_statistics, true} - ]}, - %% ---------------------------------------------------------------------------- %% RabbitMQ Shovel Plugin %% -- cgit v1.2.1 From 1fe49721f2e393df9c2829c7e1876bd1688cac05 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 May 2014 16:21:47 +0100 Subject: Support long timers. --- include/rabbit.hrl | 3 --- src/rabbit_amqqueue_process.erl | 4 ++-- src/rabbit_misc.erl | 25 +++++++++++++++++++++---- src/rabbit_policies.erl | 4 ++-- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 44f0931e..3629dd43 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -105,9 +105,6 @@ -define(DESIRED_HIBERNATE, 10000). -define(CREDIT_DISC_BOUND, {2000, 500}). -%% This is dictated by `erlang:send_after' on which we depend to implement TTL. --define(MAX_EXPIRY_TIMER, 4294967295). - -define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). -define(DELETED_HEADER, <<"BCC">>). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9b785303..753d8e15 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -385,12 +385,12 @@ ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined, V when V > 0 -> V + 999; %% always fire later _ -> 0 end) div 1000, - TRef = erlang:send_after(After, self(), {drop_expired, Version}), + TRef = rabbit_misc:send_after(After, self(), {drop_expired, Version}), State#q{ttl_timer_ref = TRef, ttl_timer_expiry = Expiry}; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, ttl_timer_expiry = TExpiry}) when Expiry + 1000 < TExpiry -> - case erlang:cancel_timer(TRef) of + case rabbit_misc:cancel_timer(TRef) of false -> State; _ -> ensure_ttl_timer(Expiry, State#q{ttl_timer_ref = undefined}) end; diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 58e93a3f..6f10b43b 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -67,7 +67,7 @@ -export([check_expiry/1]). -export([base64url/1]). -export([interval_operation/4]). --export([ensure_timer/4, stop_timer/2]). +-export([ensure_timer/4, stop_timer/2, send_after/3]). -export([get_parent/0]). -export([store_proc_name/1, store_proc_name/2]). -export([moving_average/4]). @@ -94,6 +94,7 @@ fun ((atom(), [term()]) -> [{digraph:vertex(), digraph_label()}])). -type(graph_edge_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph:vertex()}])). +-type(tref() :: {'erlang', reference()} | {timer, timer:tref()}). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) -> rabbit_framing:amqp_method_name()). @@ -245,6 +246,8 @@ -> {any(), non_neg_integer()}). -spec(ensure_timer/4 :: (A, non_neg_integer(), non_neg_integer(), any()) -> A). -spec(stop_timer/2 :: (A, non_neg_integer()) -> A). +-spec(send_after/3 :: (non_neg_integer(), pid(), any()) -> tref()). +-spec(cancel_timer/1 :: (tref()) -> 'ok'). -spec(get_parent/0 :: () -> pid()). -spec(store_proc_name/2 :: (atom(), rabbit_types:proc_name()) -> ok). -spec(store_proc_name/1 :: (rabbit_types:proc_type_and_name()) -> ok). @@ -1012,7 +1015,6 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. -check_expiry(N) when N > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, N}}; check_expiry(N) when N < 0 -> {error, {value_negative, N}}; check_expiry(_N) -> ok. @@ -1040,7 +1042,7 @@ interval_operation({M, F, A}, MaxRatio, IdealInterval, LastInterval) -> ensure_timer(State, Idx, After, Msg) -> case element(Idx, State) of - undefined -> TRef = erlang:send_after(After, self(), Msg), + undefined -> TRef = send_after(After, self(), Msg), setelement(Idx, State, TRef); _ -> State end. @@ -1048,12 +1050,27 @@ ensure_timer(State, Idx, After, Msg) -> stop_timer(State, Idx) -> case element(Idx, State) of undefined -> State; - TRef -> case erlang:cancel_timer(TRef) of + TRef -> case cancel_timer(TRef) of %% TODO bug 25393 comment 3 false -> State; _ -> setelement(Idx, State, undefined) end end. +%% timer:send_after/3 goes through a single timer process but allows +%% long delays. erlang:send_after/3 does not have a bottleneck but +%% only allows max 2^32-1 millis. +-define(MAX_ERLANG_SEND_AFTER, 4294967295). +send_after(Millis, Pid, Msg) when Millis > ?MAX_ERLANG_SEND_AFTER -> + {ok, Ref} = timer:send_after(Millis, Pid, Msg), + {timer, Ref}; +send_after(Millis, Pid, Msg) -> + {erlang, erlang:send_after(Millis, Pid, Msg)}. + +cancel_timer({erlang, Ref}) -> erlang:cancel_timer(Ref), + ok; +cancel_timer({timer, Ref}) -> {ok, cancel} = timer:cancel(Ref), + ok. + store_proc_name(Type, ProcName) -> store_proc_name({Type, ProcName}). store_proc_name(TypeProcName) -> put(process_name, TypeProcName). diff --git a/src/rabbit_policies.erl b/src/rabbit_policies.erl index fe2b766f..3558cf98 100644 --- a/src/rabbit_policies.erl +++ b/src/rabbit_policies.erl @@ -61,13 +61,13 @@ validate_policy0(<<"dead-letter-routing-key">>, Value) -> {error, "~p is not a valid dead letter routing key", [Value]}; validate_policy0(<<"message-ttl">>, Value) - when is_integer(Value), Value >= 0, Value =< ?MAX_EXPIRY_TIMER -> + when is_integer(Value), Value >= 0 -> ok; validate_policy0(<<"message-ttl">>, Value) -> {error, "~p is not a valid message TTL", [Value]}; validate_policy0(<<"expires">>, Value) - when is_integer(Value), Value >= 1, Value =< ?MAX_EXPIRY_TIMER -> + when is_integer(Value), Value >= 1 -> ok; validate_policy0(<<"expires">>, Value) -> {error, "~p is not a valid queue expiry", [Value]}; -- cgit v1.2.1 From 6c8f0d41197df54b6e521048207ca53e8456ef05 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 May 2014 17:31:35 +0100 Subject: Oops --- src/rabbit_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 6f10b43b..31648372 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -67,7 +67,7 @@ -export([check_expiry/1]). -export([base64url/1]). -export([interval_operation/4]). --export([ensure_timer/4, stop_timer/2, send_after/3]). +-export([ensure_timer/4, stop_timer/2, send_after/3, cancel_timer/1]). -export([get_parent/0]). -export([store_proc_name/1, store_proc_name/2]). -export([moving_average/4]). -- cgit v1.2.1 From 359057492421c9ad42a2e466c9eb19384e7da0ad Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 May 2014 11:06:54 +0100 Subject: Display errors on failure. --- quickcheck | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quickcheck b/quickcheck index b5382d75..40f13091 100755 --- a/quickcheck +++ b/quickcheck @@ -17,7 +17,8 @@ main([NodeStr, ModStr, TrialsStr]) -> case rpc:call(Node, proper, module, [Mod] ++ [[{numtests, Trials}, {constraint_tries, 200}]]) of [] -> ok; - _ -> quit(1) + R -> io:format("~p.~n", [R]), + quit(1) end; {badrpc, Reason} -> io:format("Could not contact node ~p: ~p.~n", [Node, Reason]), -- cgit v1.2.1 From 124d7834fa54d0cc66f6e45750511db8fe3dee22 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 30 May 2014 12:12:01 +0100 Subject: I think we've determined that we don't need that check. --- src/rabbit_misc.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 31648372..5f23caf8 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -1050,10 +1050,8 @@ ensure_timer(State, Idx, After, Msg) -> stop_timer(State, Idx) -> case element(Idx, State) of undefined -> State; - TRef -> case cancel_timer(TRef) of %% TODO bug 25393 comment 3 - false -> State; - _ -> setelement(Idx, State, undefined) - end + TRef -> cancel_timer(TRef), + setelement(Idx, State, undefined) end. %% timer:send_after/3 goes through a single timer process but allows -- cgit v1.2.1 From 679942aed65fcabc67a9cac8991b4e6c0225828a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 3 Jun 2014 17:12:01 +0100 Subject: Remove some unneeded complexity. --- src/rabbit_mirror_queue_slave.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 11d6a79c..cc06ae44 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -653,8 +653,9 @@ next_state(State = #state{backing_queue = BQ, backing_queue_state = BQS}) -> timed -> {ensure_sync_timer(State1), 0 } end. -backing_queue_timeout(State = #state { backing_queue = BQ }) -> - run_backing_queue(BQ, fun (M, BQS) -> M:timeout(BQS) end, State). +backing_queue_timeout(State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + State#state{backing_queue_state = BQ:timeout(BQS)}. ensure_sync_timer(State) -> rabbit_misc:ensure_timer(State, #state.sync_timer_ref, -- cgit v1.2.1 From 7d6c1d8b723a84b8b242fc8d1ff6cbb281a15d24 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 5 Jun 2014 16:31:57 +0100 Subject: Remove sync_notify, it doesn't seem to be needed. --- src/rabbit_event.erl | 24 ++++-------------------- src/rabbit_plugins.erl | 2 +- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index d3cd1a63..b867223b 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -23,7 +23,6 @@ ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). -export([stats_level/2, if_enabled/3]). -export([notify/2, notify/3, notify_if/3]). --export([sync_notify/2, sync_notify/3]). %%---------------------------------------------------------------------------- @@ -62,9 +61,6 @@ -spec(notify/2 :: (event_type(), event_props()) -> 'ok'). -spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok'). -spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok'). --spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok'). --spec(sync_notify/3 :: (event_type(), event_props(), - reference() | 'none') -> 'ok'). -endif. @@ -149,19 +145,7 @@ notify_if(false, _Type, _Props) -> ok. notify(Type, Props) -> notify(Type, Props, none). notify(Type, Props, Ref) -> - do_notify(notify, #event{type = Type, - props = Props, - reference = Ref, - timestamp = os:timestamp()}). - -sync_notify(Type, Props) -> sync_notify(Type, Props, none). - -sync_notify(Type, Props, Ref) -> - do_notify(sync_notify, #event{type = Type, - props = Props, - reference = Ref, - timestamp = os:timestamp()}). - -do_notify(F, Event) -> - apply(gen_event, F, [?MODULE, Event]). - + gen_event:notify(?MODULE, #event{type = Type, + props = Props, + reference = Ref, + timestamp = os:timestamp()}). diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 2f10e0a0..f6860424 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -47,7 +47,7 @@ disable(Plugins) -> RunningApps = rabbit_misc:which_applications(), ToDisable = [P || P <- Plugins, proplists:is_defined(P, RunningApps)], - ok = rabbit_event:sync_notify(plugins_changed, [{disabled, ToDisable}]), + ok = rabbit_event:notify(plugins_changed, [{disabled, ToDisable}]), rabbit:stop_apps(ToDisable). %% @doc Prepares the file system and installs all enabled plugins. -- cgit v1.2.1 From c57a0236fc2ec7cdd96f5abd7a3f18d1e4282a59 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 5 Jun 2014 17:11:54 +0100 Subject: Oops, we do need sync_notify - reinstate it. --- src/rabbit_event.erl | 21 +++++++++++++++++---- src/rabbit_plugins.erl | 5 ++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index b867223b..a33103fd 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -23,6 +23,7 @@ ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). -export([stats_level/2, if_enabled/3]). -export([notify/2, notify/3, notify_if/3]). +-export([sync_notify/2, sync_notify/3]). %%---------------------------------------------------------------------------- @@ -61,6 +62,9 @@ -spec(notify/2 :: (event_type(), event_props()) -> 'ok'). -spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok'). -spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok'). +-spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok'). +-spec(sync_notify/3 :: (event_type(), event_props(), + reference() | 'none') -> 'ok'). -endif. @@ -145,7 +149,16 @@ notify_if(false, _Type, _Props) -> ok. notify(Type, Props) -> notify(Type, Props, none). notify(Type, Props, Ref) -> - gen_event:notify(?MODULE, #event{type = Type, - props = Props, - reference = Ref, - timestamp = os:timestamp()}). + gen_event:notify(?MODULE, event_cons(Type, Props, Ref)). + +sync_notify(Type, Props) -> sync_notify(Type, Props, none). + +sync_notify(Type, Props, Ref) -> + gen_event:sync_notify(?MODULE, event_cons(Type, Props, Ref)). + +event_cons(Type, Props, Ref) -> + #event{type = Type, + props = Props, + reference = Ref, + timestamp = os:timestamp()}. + diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index f6860424..2dffa669 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -47,7 +47,10 @@ disable(Plugins) -> RunningApps = rabbit_misc:which_applications(), ToDisable = [P || P <- Plugins, proplists:is_defined(P, RunningApps)], - ok = rabbit_event:notify(plugins_changed, [{disabled, ToDisable}]), + %% We need sync_notify here since mgmt will attempt to look at all + %% the modules for the disabled plugins - if they are unloaded + %% that won't work. + ok = rabbit_event:sync_notify(plugins_changed, [{disabled, ToDisable}]), rabbit:stop_apps(ToDisable). %% @doc Prepares the file system and installs all enabled plugins. -- cgit v1.2.1 From 3469d0d272f11a00d8af0405bfca2d6c8c2d8fa0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 5 Jun 2014 19:14:27 +0100 Subject: Rewrite the changes to rabbit_plugins_main/enable,disable. Now we just change the plugins file exactly as we do on default, then contact the server and say "I want you to be running this". This has cut out quite a lot of code, and I am pretty sure fixed some bugs. --- src/rabbit_plugins.erl | 26 ++++---- src/rabbit_plugins_main.erl | 148 +++++++++++++++++--------------------------- 2 files changed, 69 insertions(+), 105 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 2dffa669..e139eed4 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). -export([setup/0, active/0, read_enabled/1, list/1, dependencies/3]). --export([enable/1, disable/1]). +-export([ensure/1]). %%---------------------------------------------------------------------------- @@ -32,26 +32,24 @@ -spec(read_enabled/1 :: (file:filename()) -> [plugin_name()]). -spec(dependencies/3 :: (boolean(), [plugin_name()], [#plugin{}]) -> [plugin_name()]). --spec(enable/1 :: ([plugin_name()]) -> 'ok'). --spec(disable/1 :: ([plugin_name()]) -> 'ok'). +-spec(ensure/1 :: ([plugin_name()]) -> {'ok', [atom()], [atom()]}). -endif. %%---------------------------------------------------------------------------- -enable(Plugins) -> - prepare_plugins(Plugins), - rabbit:start_apps(Plugins), - ok = rabbit_event:notify(plugins_changed, [{enabled, Plugins}]). - -disable(Plugins) -> - RunningApps = rabbit_misc:which_applications(), - ToDisable = [P || P <- Plugins, - proplists:is_defined(P, RunningApps)], +ensure(Wanted) -> + Current = active(), + Start = Wanted -- Current, + Stop = Current -- Wanted, + prepare_plugins(Start), %% We need sync_notify here since mgmt will attempt to look at all %% the modules for the disabled plugins - if they are unloaded %% that won't work. - ok = rabbit_event:sync_notify(plugins_changed, [{disabled, ToDisable}]), - rabbit:stop_apps(ToDisable). + ok = rabbit_event:notify(plugins_changed, [{enabled, Start}, + {disabled, Stop}]), + rabbit:start_apps(Start), + rabbit:stop_apps(Stop), + {ok, Start, Stop}. %% @doc Prepares the file system and installs all enabled plugins. setup() -> diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 3b9d30f2..555ed590 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -89,6 +89,13 @@ start() -> {error_string, Reason} -> print_error("~s", [Reason]), rabbit_misc:quit(2); + {badrpc, {'EXIT', Reason}} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {badrpc, Reason} -> + print_error("unable to connect to node ~w: ~w", [Node, Reason]), + print_badrpc_diagnostics([Node]), + rabbit_misc:quit(2); Other -> print_error("~p", [Other]), rabbit_misc:quit(2) @@ -124,26 +131,10 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> end, AllPlugins = rabbit_plugins:list(PluginsDir), Enabled = rabbit_plugins:read_enabled(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:dependencies(false, - Enabled, AllPlugins), + ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), - ExplicitlyEnabled = lists:usort(Enabled ++ ToEnable), - OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts), - NewEnabled = - case OfflineOnly of - true -> ToEnable -- Enabled; - false -> - case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> rpc_failure(Node); - [] -> ExplicitlyEnabled; - ActiveList -> - EnabledSet = sets:from_list(ExplicitlyEnabled), - ActiveSet = sets:from_list(ActiveList), - Intersect = sets:intersection(EnabledSet, ActiveSet), - sets:to_list(sets:subtract(EnabledSet, Intersect)) - end - end, + NewEnabled = lists:usort(Enabled ++ ToEnable), NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), MissingDeps = (NewImplicitlyEnabled -- plugin_names(AllPlugins)) -- Missing, @@ -155,22 +146,23 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> fmt_missing("plugins", Missing) ++ fmt_missing("dependencies", MissingDeps)}) end, - write_enabled_plugins(PluginsFile, ExplicitlyEnabled), - case NewEnabled -- (ImplicitlyEnabled -- ExplicitlyEnabled) of + write_enabled_plugins(PluginsFile, NewEnabled), + case NewEnabled -- ImplicitlyEnabled of [] -> io:format("Plugin configuration unchanged.~n"); _ -> print_list("The following plugins have been enabled:", - NewEnabled), - action_change(OfflineOnly, Node, enable, NewEnabled) - end; + NewImplicitlyEnabled -- ImplicitlyEnabled) + end, + action_change(Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled); action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> case ToDisable0 of [] -> throw({error_string, "Not enough arguments for 'disable'"}); _ -> ok end, - ToDisable = [list_to_atom(Name) || Name <- ToDisable0], - Enabled = rabbit_plugins:read_enabled(PluginsFile), AllPlugins = rabbit_plugins:list(PluginsDir), + Enabled = rabbit_plugins:read_enabled(PluginsFile), + ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), + ToDisable = [list_to_atom(Name) || Name <- ToDisable0], Missing = ToDisable -- plugin_names(AllPlugins), case Missing of [] -> ok; @@ -178,44 +170,25 @@ action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> Missing) end, ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins), - OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts), - Active = - case OfflineOnly of - true -> Enabled; - false -> case rpc:call(Node, rabbit_plugins, active, - [], ?RPC_TIMEOUT) of - {badrpc, _} -> rpc_failure(Node); - [] -> Enabled; - ActiveList -> ActiveList - end - end, NewEnabled = Enabled -- ToDisableDeps, - case length(Active) =:= length(NewEnabled) of + NewImplicitlyEnabled = rabbit_plugins:dependencies(false, + NewEnabled, AllPlugins), + case length(Enabled) =:= length(NewEnabled) of true -> io:format("Plugin configuration unchanged.~n"); - false -> ImplicitlyEnabled = - rabbit_plugins:dependencies(false, Active, AllPlugins), - NewImplicitlyEnabled = - rabbit_plugins:dependencies(false, - NewEnabled, AllPlugins), - Disabled = ImplicitlyEnabled -- NewImplicitlyEnabled, - print_list("The following plugins have been disabled:", - Disabled), - write_enabled_plugins(PluginsFile, NewEnabled), - action_change(OfflineOnly, Node, disable, Disabled) - end. + false -> print_list("The following plugins have been disabled:", + ImplicitlyEnabled -- NewImplicitlyEnabled), + write_enabled_plugins(PluginsFile, NewEnabled) + end, + action_change(Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled). %%---------------------------------------------------------------------------- -rpc_failure(Node) -> - RpcMsg = rabbit_nodes:diagnostics([Node]), - Msg = io_lib:format("Unable to contact ~p~n" - "To apply these changes anyway, " - "try again with --offline~n" - "~s", [Node, RpcMsg]), - throw({error_string, Msg}). +fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). -print_error(Format, Args) -> - rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). +print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). + +print_badrpc_diagnostics(Nodes) -> + fmt_stderr(rabbit_nodes:diagnostics(Nodes), []). usage() -> io:format("~s", [rabbit_plugins_usage:usage()]), @@ -240,9 +213,8 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> AllEnabled = rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins), EnabledImplicitly = AllEnabled -- EnabledExplicitly, - Running = case rpc:call(Node, rabbit_plugins, active, - [], ?RPC_TIMEOUT) of - {badrpc, _} -> AllEnabled; + Running = case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of + {badrpc, _} -> []; Active -> Active end, Missing = [#plugin{name = Name, dependencies = []} || @@ -333,35 +305,29 @@ write_enabled_plugins(PluginsFile, Plugins) -> PluginsFile, Reason}}) end. -action_change(true, _Node, Action, _Targets) -> - io:format("Offline Mode: No plugins were ~p.~n" - "Please (re)start the broker to apply your changes.~n", - [case Action of - enable -> started; - disable -> stopped - end]); -action_change(false, Node, Action, Targets) -> - rpc_call(Node, rabbit_plugins, Action, [Targets]). - -rpc_call(Node, Mod, Action, Args) -> - io:format("Changing plugin configuration on ~p.", [Node]), - AsyncKey = rpc:async_call(Node, Mod, Action, Args), - rpc_progress(AsyncKey, Node, Action). - -rpc_progress(Key, Node, Action) -> - case rpc:nb_yield(Key, 1000) of - timeout -> io:format("."), - rpc_progress(Key, Node, Action); - {value, {badrpc, nodedown}} -> - io:format(". error.~nUnable to contact ~p.~n ", [Node]), - io:format("Please start the broker to apply " - "your changes.~n"); - {value, ok} -> - io:format(". done.~n", []); - {value, Error} -> - io:format(". error.~nUnable to ~p plugin(s).~n" - "Please restart the broker to apply your changes.~n" - "Error: ~p~n", - [Action, Error]) +action_change(Opts, Node, Old, New) -> + action_change0(proplists:get_bool(?OFFLINE_OPT, Opts), Node, Old, New). + +action_change0(true, _Node, Same, Same) -> + %% Definitely nothing to do + ok; +action_change0(true, _Node, _Old, _New) -> + io:format("Offline change; changes will take effect at broker restart.~n"); +action_change0(false, Node, _Old, New) -> + %% Don't care what the Old was in the plugins file, that might not + %% match what the server is running - so tell it to ensure we are + %% running the right apps even if "nothing has changed". + rpc_call(Node, rabbit_plugins, ensure, [New]). + +rpc_call(Node, Mod, Fun, Args) -> + io:format("Checking plugin configuration on ~p...", [Node]), + case rpc:call(Node, Mod, Fun, Args) of + {ok, [], []} -> + io:format(" ok.~n", []); + {ok, Start, Stop} -> + io:format(" started ~p, stopped ~p.~n", + [length(Start), length(Stop)]); + {badrpc, _} = Error -> + io:format(" failed.~n", []), + Error end. - -- cgit v1.2.1 From bfa9f7706ead1b581cdec498e05b0b330b2ac3e3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jun 2014 14:46:18 +0100 Subject: Remove a couple of buggy changes from default, make start_apps/1 and stop_apps/1 more symmetrical in use. --- src/rabbit.erl | 71 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 4de783fe..9f78a58a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -338,23 +338,6 @@ broker_start() -> start_apps(ToBeLoaded), ok = log_broker_started(rabbit_plugins:active()). -handle_app_error(Term) -> - fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> - throw({Term, App, ExitReason}); - (App, Reason) -> - throw({Term, App, Reason}) - end. - -start_apps(Apps) -> - app_utils:load_applications(Apps), - StartupApps = app_utils:app_dependency_order(Apps, false), - case whereis(rabbit_boot) of - undefined -> run_boot_steps(Apps); - _ -> ok - end, - ok = app_utils:start_applications(StartupApps, - handle_app_error(could_not_start)). - start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), case catch register(rabbit_boot, Marker) of @@ -379,13 +362,12 @@ start_it(StartFun) -> end. stop() -> - Apps = app_shutdown_order(), case whereis(rabbit_boot) of undefined -> ok; - _ -> app_utils:wait_for_applications(Apps) + _ -> await_startup() end, rabbit_log:info("Stopping RabbitMQ~n"), - ok = app_utils:stop_applications(Apps). + stop_apps(app_shutdown_order()). stop_and_halt() -> try @@ -396,21 +378,42 @@ stop_and_halt() -> end, ok. +start_apps(Apps) -> + app_utils:load_applications(Apps), + OrderedApps = app_utils:app_dependency_order(Apps, false), + case lists:member(rabbit, Apps) of + false -> run_boot_steps(Apps); %% plugin activation + true -> ok %% will run during start of rabbit app + end, + ok = app_utils:start_applications(OrderedApps, + handle_app_error(could_not_start)). + 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] + ok = app_utils:stop_applications( + Apps, handle_app_error(error_during_shutdown)), + case lists:member(rabbit, Apps) of + false -> run_cleanup_steps(Apps); %% plugin deactivation + true -> ok %% it's all going anyway + end, + unload_apps(Apps), + ok. + +unload_apps(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]. + +handle_app_error(Term) -> + fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> + throw({Term, App, ExitReason}); + (App, Reason) -> + throw({Term, App, Reason}) end. run_cleanup_steps(Apps) -> -- cgit v1.2.1 From 2c61b5fa51ea72915471bc8f6c73be75c48d8f1d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jun 2014 15:42:31 +0100 Subject: Documentation. --- docs/rabbitmq-plugins.1.xml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index 3b67d0e6..d3268af3 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -40,6 +40,7 @@ rabbitmq-plugins + -n node command command options @@ -132,9 +133,13 @@ - enable plugin ... + enable --offline plugin ... + + --offline + Just modify the enabled plugins file. + plugin One or more plugins to enable. @@ -142,7 +147,9 @@ Enables the specified plugins and all their - dependencies. + dependencies. This will update the enabled plugins file + and then connect to the broker and ensure it is running + all enabled plugins. For example: @@ -156,17 +163,23 @@ - disable plugin ... + disable --offline plugin ... + + --offline + Just modify the enabled plugins file. + plugin One or more plugins to disable. - Disables the specified plugins and all plugins that - depend on them. + Disables the specified plugins and all their + dependencies. This will update the enabled plugins file + and then connect to the broker and ensure it is running + all enabled plugins. For example: -- cgit v1.2.1 From cd35bd0a344ea92d52110ef14c5120f0a808b152 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jun 2014 17:28:45 +0100 Subject: Move the code-unloading thing to rabbit_plugins, and remove the unpacked plugin after doing it, so that it actually allows us to reload modified plugins. --- src/rabbit.erl | 12 ------------ src/rabbit_plugins.erl | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 9f78a58a..90181619 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -395,20 +395,8 @@ stop_apps(Apps) -> false -> run_cleanup_steps(Apps); %% plugin deactivation true -> ok %% it's all going anyway end, - unload_apps(Apps), ok. -unload_apps(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]. - handle_app_error(Term) -> fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) -> throw({Term, App, ExitReason}); diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index e139eed4..0eabf4dd 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -49,6 +49,7 @@ ensure(Wanted) -> {disabled, Stop}]), rabbit:start_apps(Start), rabbit:stop_apps(Stop), + clean_plugins(Stop), {ok, Start, Stop}. %% @doc Prepares the file system and installs all enabled plugins. @@ -153,6 +154,20 @@ prepare_plugins(Enabled) -> [prepare_dir_plugin(PluginAppDescPath) || PluginAppDescPath <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. +clean_plugins(Plugins) -> + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + [clean_plugin(Plugin, ExpandDir) || Plugin <- Plugins]. + +clean_plugin(Plugin, ExpandDir) -> + {ok, Mods} = application:get_key(Plugin, modules), + application:unload(Plugin), + [begin + code:soft_purge(Mod), + code:delete(Mod), + false = code:is_loaded(Mod) + end || Mod <- Mods], + delete_recursively(rabbit_misc:format("~s/~s", [ExpandDir, Plugin])). + prepare_dir_plugin(PluginAppDescPath) -> code:add_path(filename:dirname(PluginAppDescPath)), list_to_atom(filename:basename(PluginAppDescPath, ".app")). -- cgit v1.2.1 From a534dc355037d373aaf0b63215beae6775173bf9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jun 2014 13:45:43 +0100 Subject: Make decorators field in exchange and queue transient, i.e. we recalcualte it on recovery in case plugins have changed. Also document a bit more clearly what the lifecycle is of the different fields in these records since that area is becoming confusing. --- include/rabbit.hrl | 23 +++++++++++++++++------ src/rabbit_amqqueue.erl | 31 +++++++++++++++++-------------- src/rabbit_exchange.erl | 16 +++++++++------- src/rabbit_policy.erl | 6 ++---- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 5ac3197e..a0e96d87 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -39,13 +39,24 @@ -record(resource, {virtual_host, kind, name}). --record(exchange, {name, type, durable, auto_delete, internal, arguments, - scratches, policy, decorators}). --record(exchange_serial, {name, next}). +%% fields described as 'transient' here are cleared when writing to +%% rabbit_durable_ +-record(exchange, { + name, type, durable, auto_delete, internal, arguments, %% immutable + scratches, %% durable, explicitly updated via update_scratch/3 + policy, %% durable, implicitly updated when policy changes + decorators}). %% transient, recalculated in store/1 (i.e. recovery) + +-record(amqqueue, { + name, durable, auto_delete, exclusive_owner = none, %% immutable + arguments, %% immutable + pid, %% durable (just so we know home node) + slave_pids, sync_slave_pids, %% transient + policy, %% durable, implicit update as above + gm_pids, %% transient + decorators}). %% transient, recalculated as above --record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, - arguments, pid, slave_pids, sync_slave_pids, policy, - gm_pids, decorators}). +-record(exchange_serial, {name, next}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 1aba7ecb..8bc0cab6 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -254,15 +254,16 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> %% effect) this might not be possible to satisfy. declare(QueueName, Durable, AutoDelete, Args, Owner, Node) -> ok = check_declare_arguments(QueueName, Args), - Q = rabbit_policy:set(#amqqueue{name = QueueName, - durable = Durable, - auto_delete = AutoDelete, - arguments = Args, - exclusive_owner = Owner, - pid = none, - slave_pids = [], - sync_slave_pids = [], - gm_pids = []}), + Q = rabbit_queue_decorator:set( + rabbit_policy:set(#amqqueue{name = QueueName, + durable = Durable, + auto_delete = AutoDelete, + arguments = Args, + exclusive_owner = Owner, + pid = none, + slave_pids = [], + sync_slave_pids = [], + gm_pids = []})), Node = rabbit_mirror_queue_misc:initial_queue_node(Q, Node), gen_server2:call(start_queue_process(Node, Q), {init, new}, infinity). @@ -308,12 +309,14 @@ store_queue(Q = #amqqueue{durable = true}) -> ok = mnesia:write(rabbit_durable_queue, Q#amqqueue{slave_pids = [], sync_slave_pids = [], - gm_pids = []}, write), - ok = mnesia:write(rabbit_queue, Q, write), - ok; + gm_pids = [], + decorators = []}, write), + store_queue0(Q); store_queue(Q = #amqqueue{durable = false}) -> - ok = mnesia:write(rabbit_queue, Q, write), - ok. + store_queue0(Q). + +store_queue0(Q) -> + ok = mnesia:write(rabbit_queue, rabbit_queue_decorator:set(Q), write). policy_changed(Q1 = #amqqueue{decorators = Decorators1}, Q2 = #amqqueue{decorators = Decorators2}) -> diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 4d4a2a58..f1b2d694 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -158,12 +158,13 @@ serial(#exchange{name = XName} = X) -> end. declare(XName, Type, Durable, AutoDelete, Internal, Args) -> - X = rabbit_policy:set(#exchange{name = XName, - type = Type, - durable = Durable, - auto_delete = AutoDelete, - internal = Internal, - arguments = Args}), + X = rabbit_exchange_decorator:set( + rabbit_policy:set(#exchange{name = XName, + type = Type, + durable = Durable, + auto_delete = AutoDelete, + internal = Internal, + arguments = Args})), XT = type_to_module(Type), %% We want to upset things if it isn't ok ok = XT:validate(X), @@ -195,7 +196,8 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> map_create_tx(true) -> transaction; map_create_tx(false) -> none. -store(X) -> ok = mnesia:write(rabbit_exchange, X, write). +store(X) -> ok = mnesia:write( + rabbit_exchange, rabbit_exchange_decorator:set(X), write). %% Used with binaries sent over the wire; the type may not exist. check_type(TypeBin) -> diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 0a69fb32..5a1e5452 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -46,10 +46,8 @@ name(#exchange{policy = Policy}) -> name0(Policy). name0(undefined) -> none; name0(Policy) -> pget(name, Policy). -set(Q = #amqqueue{name = Name}) -> rabbit_queue_decorator:set( - Q#amqqueue{policy = set0(Name)}); -set(X = #exchange{name = Name}) -> rabbit_exchange_decorator:set( - X#exchange{policy = set0(Name)}). +set(Q = #amqqueue{name = Name}) -> Q#amqqueue{policy = set0(Name)}; +set(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. set0(Name = #resource{virtual_host = VHost}) -> match(Name, list(VHost)). -- cgit v1.2.1 From a76f071596d22abe6aa0190bb234a5d90b2199b3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jun 2014 13:47:05 +0100 Subject: Inline those since they're special purpose, and add a missed call to queue_decorator:set/1 (which won't matter until we do another policy-invalidating upgrade, but better to catch it now...) --- src/rabbit_policy.erl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 5a1e5452..f5d03360 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -51,10 +51,6 @@ set(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. set0(Name = #resource{virtual_host = VHost}) -> match(Name, list(VHost)). -set(Q = #amqqueue{name = Name}, Ps) -> Q#amqqueue{policy = match(Name, Ps)}; -set(X = #exchange{name = Name}, Ps) -> rabbit_exchange_decorator:set( - X#exchange{policy = match(Name, Ps)}). - get(Name, #amqqueue{policy = Policy}) -> get0(Name, Policy); get(Name, #exchange{policy = Policy}) -> get0(Name, Policy); %% Caution - SLOW. @@ -102,12 +98,18 @@ recover0() -> Policies = list(), [rabbit_misc:execute_mnesia_transaction( fun () -> - mnesia:write(rabbit_durable_exchange, set(X, Policies), write) - end) || X <- Xs], + mnesia:write( + rabbit_durable_exchange, + rabbit_exchange_decorator:set( + X#exchange{policy = match(Name, Policies)}), write) + end) || X = #exchange{name = Name} <- Xs], [rabbit_misc:execute_mnesia_transaction( fun () -> - mnesia:write(rabbit_durable_queue, set(Q, Policies), write) - end) || Q <- Qs], + mnesia:write( + rabbit_durable_queue, + rabbit_queue_decorator:set( + Q#amqqueue{policy = match(Name, Policies)}), write) + end) || Q = #amqqueue{name = Name} <- Qs], ok. invalid_file() -> -- cgit v1.2.1 From 60096a3faff2228b616c408477bd9ff6df9db9e6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jun 2014 15:37:35 +0100 Subject: Ensure rabbit_exchange funnels all updates through a single function, like rabbit_amqqueue already does. Ensure that decorators is set to 'undefined' in the durable record so any inadvertent read without setting it will cause an immediate explosion not a subtle bug. And remove report_missing_decorators(), there is no longer any sane context in which it does anything useful. --- src/rabbit_amqqueue.erl | 2 +- src/rabbit_exchange.erl | 55 ++++++++++++++++++++----------------------------- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8bc0cab6..b0b78257 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -310,7 +310,7 @@ store_queue(Q = #amqqueue{durable = true}) -> Q#amqqueue{slave_pids = [], sync_slave_pids = [], gm_pids = [], - decorators = []}, write), + decorators = undefined}, write), store_queue0(Q); store_queue(Q = #amqqueue{durable = false}) -> store_queue0(Q). diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index f1b2d694..350de2a8 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -106,24 +106,15 @@ recover() -> mnesia:read({rabbit_exchange, XName}) =:= [] end, fun (X, Tx) -> - case Tx of - true -> store(X); - false -> ok - end, - callback(X, create, map_create_tx(Tx), [X]) + X1 = case Tx of + true -> store0(X); + false -> rabbit_exchange_decorator:set(X) + end, + callback(X1, create, map_create_tx(Tx), [X1]) end, rabbit_durable_exchange), - report_missing_decorators(Xs), [XName || #exchange{name = XName} <- Xs]. -report_missing_decorators(Xs) -> - Mods = lists:usort(lists:append([rabbit_exchange_decorator:select(raw, D) || - #exchange{decorators = D} <- Xs])), - case [M || M <- Mods, code:which(M) =:= non_existing] of - [] -> ok; - M -> rabbit_log:warning("Missing exchange decorators: ~p~n", [M]) - end. - callback(X = #exchange{type = XType, decorators = Decorators}, Fun, Serial0, Args) -> Serial = if is_function(Serial0) -> Serial0; @@ -172,13 +163,7 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> fun () -> case mnesia:wread({rabbit_exchange, XName}) of [] -> - store(X), - ok = case Durable of - true -> mnesia:write(rabbit_durable_exchange, - X, write); - false -> ok - end, - {new, X}; + {new, store(X)}; [ExistingX] -> {existing, ExistingX} end @@ -196,8 +181,19 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> map_create_tx(true) -> transaction; map_create_tx(false) -> none. -store(X) -> ok = mnesia:write( - rabbit_exchange, rabbit_exchange_decorator:set(X), write). + +store(X = #exchange{durable = true}) -> + mnesia:write(rabbit_durable_exchange, X#exchange{decorators = undefined}, + write), + store0(X); +store(X = #exchange{durable = false}) -> + store0(X). + +store0(X) -> + X1 = rabbit_exchange_decorator:set(X), + ok = mnesia:write(rabbit_exchange, rabbit_exchange_decorator:set(X1), + write), + X1. %% Used with binaries sent over the wire; the type may not exist. check_type(TypeBin) -> @@ -291,16 +287,9 @@ update_scratch(Name, App, Fun) -> update(Name, Fun) -> case mnesia:wread({rabbit_exchange, Name}) of - [X = #exchange{durable = Durable}] -> - X1 = Fun(X), - ok = mnesia:write(rabbit_exchange, X1, write), - case Durable of - true -> ok = mnesia:write(rabbit_durable_exchange, X1, write); - _ -> ok - end, - X1; - [] -> - not_found + [X] -> X1 = Fun(X), + store(X1); + [] -> not_found end. info_keys() -> ?INFO_KEYS. -- cgit v1.2.1 From de82a62350f96ae88ff2c4660e269506176d82a6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jun 2014 17:48:30 +0100 Subject: Support updating decorators correctly when plugins start and stop. --- src/rabbit_amqqueue.erl | 19 +++++++++++++++---- src/rabbit_exchange.erl | 25 ++++++++++++++++++++----- src/rabbit_exchange_decorator.erl | 24 +++++++++++++++++++++++- src/rabbit_queue_decorator.erl | 23 ++++++++++++++++++++++- 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index b0b78257..7a26ddec 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -30,7 +30,7 @@ -export([notify_sent/2, notify_sent_queue_down/1, resume/2]). -export([notify_down_all/2, activate_limit_all/2, credit/5]). -export([on_node_down/1]). --export([update/2, store_queue/1, policy_changed/2]). +-export([update/2, store_queue/1, update_decorators/1, policy_changed/2]). -export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1, cancel_sync_mirrors/1]). @@ -177,6 +177,7 @@ -spec(on_node_down/1 :: (node()) -> 'ok'). -spec(pseudo_queue/2 :: (name(), pid()) -> rabbit_types:amqqueue()). -spec(store_queue/1 :: (rabbit_types:amqqueue()) -> 'ok'). +-spec(update_decorators/1 :: (name()) -> 'ok'). -spec(policy_changed/2 :: (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -spec(start_mirroring/1 :: (pid()) -> 'ok'). @@ -311,13 +312,23 @@ store_queue(Q = #amqqueue{durable = true}) -> sync_slave_pids = [], gm_pids = [], decorators = undefined}, write), - store_queue0(Q); + store_queue_ram(Q); store_queue(Q = #amqqueue{durable = false}) -> - store_queue0(Q). + store_queue_ram(Q). -store_queue0(Q) -> +store_queue_ram(Q) -> ok = mnesia:write(rabbit_queue, rabbit_queue_decorator:set(Q), write). +update_decorators(Name) -> + rabbit_misc:execute_mnesia_transaction( + fun() -> + case mnesia:wread({rabbit_queue, Name}) of + [Q] -> store_queue_ram(Q), + ok; + [] -> ok + end + end). + policy_changed(Q1 = #amqqueue{decorators = Decorators1}, Q2 = #amqqueue{decorators = Decorators2}) -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2), diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 350de2a8..2e0566b7 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -20,7 +20,8 @@ -export([recover/0, policy_changed/2, callback/4, declare/6, assert_equivalence/6, assert_args_equivalence/2, check_type/1, - lookup/1, lookup_or_die/1, list/1, lookup_scratch/2, update_scratch/3, + lookup/1, lookup_or_die/1, list/0, list/1, lookup_scratch/2, + update_scratch/3, update_decorators/1, info_keys/0, info/1, info/2, info_all/1, info_all/2, route/2, delete/2, validate_binding/2]). %% these must be run inside a mnesia tx @@ -61,6 +62,7 @@ -spec(lookup_or_die/1 :: (name()) -> rabbit_types:exchange() | rabbit_types:channel_exit()). +-spec(list/0 :: () -> [rabbit_types:exchange()]). -spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:exchange()]). -spec(lookup_scratch/2 :: (name(), atom()) -> rabbit_types:ok(term()) | @@ -70,6 +72,7 @@ (name(), fun((rabbit_types:exchange()) -> rabbit_types:exchange())) -> not_found | rabbit_types:exchange()). +-spec(update_decorators/1 :: (name()) -> 'ok'). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). -spec(info/2 :: @@ -107,7 +110,7 @@ recover() -> end, fun (X, Tx) -> X1 = case Tx of - true -> store0(X); + true -> store_ram(X); false -> rabbit_exchange_decorator:set(X) end, callback(X1, create, map_create_tx(Tx), [X1]) @@ -185,11 +188,11 @@ map_create_tx(false) -> none. store(X = #exchange{durable = true}) -> mnesia:write(rabbit_durable_exchange, X#exchange{decorators = undefined}, write), - store0(X); + store_ram(X); store(X = #exchange{durable = false}) -> - store0(X). + store_ram(X). -store0(X) -> +store_ram(X) -> X1 = rabbit_exchange_decorator:set(X), ok = mnesia:write(rabbit_exchange, rabbit_exchange_decorator:set(X1), write), @@ -241,6 +244,8 @@ lookup_or_die(Name) -> {error, not_found} -> rabbit_misc:not_found(Name) end. +list() -> mnesia:dirty_match_object(rabbit_exchange, #exchange{_ = '_'}). + %% Not dirty_match_object since that would not be transactional when used in a %% tx context list(VHostPath) -> @@ -285,6 +290,16 @@ update_scratch(Name, App, Fun) -> ok end). +update_decorators(Name) -> + rabbit_misc:execute_mnesia_transaction( + fun() -> + case mnesia:wread({rabbit_exchange, Name}) of + [X] -> store_ram(X), + ok; + [] -> ok + end + end). + update(Name, Fun) -> case mnesia:wread({rabbit_exchange, Name}) of [X] -> X1 = Fun(X), diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 2f056b1b..900f9c32 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([select/2, set/1]). +-export([select/2, set/1, register/2, unregister/1]). %% This is like an exchange type except that: %% @@ -104,3 +104,25 @@ list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. cons_if_eq(Select, Select, Item, List) -> [Item | List]; cons_if_eq(_Select, _Other, _Item, List) -> List. + +register(TypeName, ModuleName) -> + rabbit_registry:register(exchange_decorator, TypeName, ModuleName), + [maybe_recover(X) || X <- rabbit_exchange:list()], + ok. + +unregister(TypeName) -> + rabbit_registry:unregister(exchange_decorator, TypeName), + [maybe_recover(X) || X <- rabbit_exchange:list()], + ok. + +maybe_recover(X = #exchange{name = Name, + decorators = Decs}) -> + #exchange{decorators = Decs1} = set(X), + Old = lists:sort(select(all, Decs)), + New = lists:sort(select(all, Decs1)), + case New of + Old -> ok; + _ -> %% TODO create a tx here for non-federation decorators + [M:create(none, X) || M <- New -- Old], + rabbit_exchange:update_decorators(Name) + end. diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl index 6205e2dc..adfe0c7f 100644 --- a/src/rabbit_queue_decorator.erl +++ b/src/rabbit_queue_decorator.erl @@ -2,7 +2,7 @@ -include("rabbit.hrl"). --export([select/1, set/1]). +-export([select/1, set/1, register/2, unregister/1]). %%---------------------------------------------------------------------------- @@ -41,3 +41,24 @@ select(Modules) -> set(Q) -> Q#amqqueue{decorators = [D || D <- list(), D:active_for(Q)]}. list() -> [M || {_, M} <- rabbit_registry:lookup_all(queue_decorator)]. + +register(TypeName, ModuleName) -> + rabbit_registry:register(queue_decorator, TypeName, ModuleName), + [maybe_recover(Q) || Q <- rabbit_amqqueue:list()], + ok. + +unregister(TypeName) -> + rabbit_registry:unregister(queue_decorator, TypeName), + [maybe_recover(Q) || Q <- rabbit_amqqueue:list()], + ok. + +maybe_recover(Q = #amqqueue{name = Name, + decorators = Decs}) -> + #amqqueue{decorators = Decs1} = set(Q), + Old = lists:sort(select(Decs)), + New = lists:sort(select(Decs1)), + case New of + Old -> ok; + _ -> [M:startup(Q) || M <- New -- Old], + rabbit_amqqueue:update_decorators(Name) + end. -- cgit v1.2.1 From 74aa990f3c72aa97b6bb5b46ba64c60e0ae3f013 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jun 2014 17:57:13 +0100 Subject: Tweak text a bit more. --- src/rabbit_plugins_main.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 555ed590..78313eee 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -320,14 +320,21 @@ action_change0(false, Node, _Old, New) -> rpc_call(Node, rabbit_plugins, ensure, [New]). rpc_call(Node, Mod, Fun, Args) -> - io:format("Checking plugin configuration on ~p...", [Node]), + io:format("Applying plugin configuration to ~s...", [Node]), case rpc:call(Node, Mod, Fun, Args) of {ok, [], []} -> - io:format(" ok.~n", []); + io:format(" nothing to do.~n", []); + {ok, Start, []} -> + io:format(" started ~b plugin~s.~n", [length(Start), plur(Start)]); + {ok, [], Stop} -> + io:format(" stopped ~b plugin~s.~n", [length(Stop), plur(Stop)]); {ok, Start, Stop} -> - io:format(" started ~p, stopped ~p.~n", - [length(Start), length(Stop)]); + io:format(" started ~b and stopped ~b plugin~s.~n", + [length(Start), length(Stop), plur(Start ++ Stop)]); {badrpc, _} = Error -> io:format(" failed.~n", []), Error end. + +plur([_]) -> ""; +plur(_) -> "s". -- cgit v1.2.1 From 55a8cdf03e0f5c1e08a236360cf35423146fa7b7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jun 2014 18:08:16 +0100 Subject: Add (deliberately) undocumented feature to just trigger the sync without updating the config file. --- src/rabbit_plugins_main.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 78313eee..d655e714 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -41,7 +41,8 @@ -define(COMMANDS, [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, {enable, [?OFFLINE_DEF]}, - {disable, [?OFFLINE_DEF]}]). + {disable, [?OFFLINE_DEF]}, + {sync, []}]). %%---------------------------------------------------------------------------- @@ -179,7 +180,13 @@ action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> ImplicitlyEnabled -- NewImplicitlyEnabled), write_enabled_plugins(PluginsFile, NewEnabled) end, - action_change(Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled). + action_change(Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled); + +action(sync, Node, X, Opts, PluginsFile, PluginsDir) -> + AllPlugins = rabbit_plugins:list(PluginsDir), + Enabled = rabbit_plugins:read_enabled(PluginsFile), + ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), + action_change(Opts, Node, ImplicitlyEnabled, ImplicitlyEnabled). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From c408795f2631cf9fa4cdb524ee9663070449d9d7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 10 Jun 2014 10:27:04 +0100 Subject: Tweak text one more time. --- src/rabbit_plugins_main.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index d655e714..981fc649 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -336,8 +336,8 @@ rpc_call(Node, Mod, Fun, Args) -> {ok, [], Stop} -> io:format(" stopped ~b plugin~s.~n", [length(Stop), plur(Stop)]); {ok, Start, Stop} -> - io:format(" started ~b and stopped ~b plugin~s.~n", - [length(Start), length(Stop), plur(Start ++ Stop)]); + io:format(" stopped ~b plugin~s and started ~b plugin~s.~n", + [length(Stop), plur(Stop), length(Start), plur(Start)]); {badrpc, _} = Error -> io:format(" failed.~n", []), Error -- cgit v1.2.1 From 18786eda7fb6352a99085af077d28efdcb6884b8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 10 Jun 2014 13:29:02 +0100 Subject: Make sure all code for both added and removed plugins is loaded when we send the notify event. --- src/rabbit_plugins.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 0eabf4dd..39639b6d 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -42,12 +42,12 @@ ensure(Wanted) -> Start = Wanted -- Current, Stop = Current -- Wanted, prepare_plugins(Start), + rabbit:start_apps(Start), %% We need sync_notify here since mgmt will attempt to look at all %% the modules for the disabled plugins - if they are unloaded %% that won't work. ok = rabbit_event:notify(plugins_changed, [{enabled, Start}, {disabled, Stop}]), - rabbit:start_apps(Start), rabbit:stop_apps(Stop), clean_plugins(Stop), {ok, Start, Stop}. -- cgit v1.2.1 From 745c703514a9388cd034920f4c33f9e503928fb5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 10 Jun 2014 16:14:13 +0100 Subject: Add online flag. Get the running broker to read its own plugins file rather than tell it which plugins to run. This necessitates various bug fixes to rabbit_plugins. --- docs/rabbitmq-plugins.1.xml | 28 +++++++++++--- src/rabbit_plugins.erl | 89 ++++++++++++++++++++++++++------------------- src/rabbit_plugins_main.erl | 54 +++++++++++++++++---------- 3 files changed, 108 insertions(+), 63 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index d3268af3..e891969f 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -133,13 +133,17 @@ - enable --offline plugin ... + enable --offline --online plugin ... --offline Just modify the enabled plugins file. + + --online + Treat failure to connect to the running broker as fatal. + plugin One or more plugins to enable. @@ -148,8 +152,12 @@ Enables the specified plugins and all their dependencies. This will update the enabled plugins file - and then connect to the broker and ensure it is running - all enabled plugins. + and then attempt to connect to the broker and ensure it is + running all enabled plugins. By default if it is not + possible to connect to the running broker (for example if + it is stopped) then a warning is displayed. Specify + --online or + --offline to change this. For example: @@ -163,13 +171,17 @@ - disable --offline plugin ... + disable --offline --online plugin ... --offline Just modify the enabled plugins file. + + --online + Treat failure to connect to the running broker as fatal. + plugin One or more plugins to disable. @@ -178,8 +190,12 @@ Disables the specified plugins and all their dependencies. This will update the enabled plugins file - and then connect to the broker and ensure it is running - all enabled plugins. + and then attempt to connect to the broker and ensure it is + running all enabled plugins. By default if it is not + possible to connect to the running broker (for example if + it is stopped) then a warning is displayed. Specify + --online or + --offline to change this. For example: diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 39639b6d..81f8c274 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -32,25 +32,34 @@ -spec(read_enabled/1 :: (file:filename()) -> [plugin_name()]). -spec(dependencies/3 :: (boolean(), [plugin_name()], [#plugin{}]) -> [plugin_name()]). --spec(ensure/1 :: ([plugin_name()]) -> {'ok', [atom()], [atom()]}). +-spec(ensure/1 :: (string()) -> {'ok', [atom()], [atom()]} | {error, any()}). -endif. %%---------------------------------------------------------------------------- -ensure(Wanted) -> - Current = active(), - Start = Wanted -- Current, - Stop = Current -- Wanted, - prepare_plugins(Start), - rabbit:start_apps(Start), - %% We need sync_notify here since mgmt will attempt to look at all - %% the modules for the disabled plugins - if they are unloaded - %% that won't work. - ok = rabbit_event:notify(plugins_changed, [{enabled, Start}, - {disabled, Stop}]), - rabbit:stop_apps(Stop), - clean_plugins(Stop), - {ok, Start, Stop}. +ensure(FileJustChanged) -> + {ok, OurFile} = application:get_env(rabbit, enabled_plugins_file), + case OurFile of + FileJustChanged -> + {ok, Dir} = application:get_env(rabbit, plugins_dir), + Enabled = read_enabled(OurFile), + Wanted = dependencies(false, Enabled, list(Dir)), + prepare_plugins(Enabled), + Current = active(), + Start = Wanted -- Current, + Stop = Current -- Wanted, + rabbit:start_apps(Start), + %% We need sync_notify here since mgmt will attempt to look at all + %% the modules for the disabled plugins - if they are unloaded + %% that won't work. + ok = rabbit_event:notify(plugins_changed, [{enabled, Start}, + {disabled, Stop}]), + rabbit:stop_apps(Stop), + clean_plugins(Stop), + {ok, Start, Stop}; + _ -> + {error, {enabled_plugins_mismatch, FileJustChanged, OurFile}} + end. %% @doc Prepares the file system and installs all enabled plugins. setup() -> @@ -70,7 +79,7 @@ setup() -> %% @doc Lists the plugins which are currently running. active() -> {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), - InstalledPlugins = [ P#plugin.name || P <- list(ExpandDir) ], + InstalledPlugins = plugin_names(list(ExpandDir)), [App || {App, _, _} <- rabbit_misc:which_applications(), lists:member(App, InstalledPlugins)]. @@ -91,7 +100,7 @@ list(PluginsDir) -> _ -> error_logger:warning_msg( "Problem reading some plugins: ~p~n", [Problems]) end, - Plugins. + ensure_dependencies(Plugins). %% @doc Read the list of enabled plugins from the supplied term file. read_enabled(PluginsFile) -> @@ -112,13 +121,8 @@ dependencies(Reverse, Sources, AllPlugins) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun ({App, _Deps}) -> [{App, App}] end, fun ({App, Deps}) -> [{App, Dep} || Dep <- Deps] end, - lists:ukeysort( - 1, [{Name, Deps} || - #plugin{name = Name, - dependencies = Deps} <- AllPlugins] ++ - [{Dep, []} || - #plugin{dependencies = Deps} <- AllPlugins, - Dep <- Deps])), + [{Name, Deps} || #plugin{name = Name, + dependencies = Deps} <- AllPlugins]), Dests = case Reverse of false -> digraph_utils:reachable(Sources, G); true -> digraph_utils:reaching(Sources, G) @@ -126,6 +130,28 @@ dependencies(Reverse, Sources, AllPlugins) -> true = digraph:delete(G), Dests. +%% Make sure we don't list OTP apps in here, and also that we create +%% fake plugins for missing dependencies. +ensure_dependencies(Plugins) -> + Names = plugin_names(Plugins), + NotThere = [Dep || #plugin{dependencies = Deps} <- Plugins, + Dep <- Deps, + not lists:member(Dep, Names)], + {OTP, Missing} = lists:partition(fun is_loadable/1, NotThere), + Plugins1 = [P#plugin{dependencies = Deps -- OTP} + || P = #plugin{dependencies = Deps} <- Plugins], + Fake = [#plugin{name = Name, + dependencies = []}|| Name <- Missing], + Plugins1 ++ Fake. + +is_loadable(App) -> + case application:load(App) of + {error, {already_loaded, _}} -> true; + ok -> application:unload(App), + true; + _ -> false + end. + %%---------------------------------------------------------------------------- prepare_plugins(Enabled) -> @@ -206,8 +232,7 @@ plugin_info(Base, {app, App0}) -> mkplugin(Name, Props, Type, Location) -> Version = proplists:get_value(vsn, Props, "0"), Description = proplists:get_value(description, Props, ""), - Dependencies = - filter_applications(proplists:get_value(applications, Props, [])), + Dependencies = proplists:get_value(applications, Props, []), #plugin{name = Name, version = Version, description = Description, dependencies = Dependencies, location = Location, type = Type}. @@ -240,18 +265,6 @@ parse_binary(Bin) -> Err -> {error, {invalid_app, Err}} end. -filter_applications(Applications) -> - [Application || Application <- Applications, - not is_available_app(Application)]. - -is_available_app(Application) -> - case application:load(Application) of - {error, {already_loaded, _}} -> true; - ok -> application:unload(Application), - true; - _ -> false - end. - plugin_names(Plugins) -> [Name || #plugin{name = Name} <- Plugins]. diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 981fc649..b7bc9ce6 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -26,6 +26,7 @@ -define(ENABLED_OPT, "-E"). -define(ENABLED_ALL_OPT, "-e"). -define(OFFLINE_OPT, "--offline"). +-define(ONLINE_OPT, "--online"). -define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). @@ -33,6 +34,7 @@ -define(ENABLED_DEF, {?ENABLED_OPT, flag}). -define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). -define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). +-define(ONLINE_DEF, {?ONLINE_OPT, flag}). -define(RPC_TIMEOUT, infinity). @@ -40,8 +42,8 @@ -define(COMMANDS, [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, - {enable, [?OFFLINE_DEF]}, - {disable, [?OFFLINE_DEF]}, + {enable, [?OFFLINE_DEF, ?ONLINE_DEF]}, + {disable, [?OFFLINE_DEF, ?ONLINE_DEF]}, {sync, []}]). %%---------------------------------------------------------------------------- @@ -153,7 +155,8 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> _ -> print_list("The following plugins have been enabled:", NewImplicitlyEnabled -- ImplicitlyEnabled) end, - action_change(Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled); + action_change( + Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> case ToDisable0 of @@ -180,13 +183,11 @@ action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> ImplicitlyEnabled -- NewImplicitlyEnabled), write_enabled_plugins(PluginsFile, NewEnabled) end, - action_change(Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled); + action_change( + Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); -action(sync, Node, X, Opts, PluginsFile, PluginsDir) -> - AllPlugins = rabbit_plugins:list(PluginsDir), - Enabled = rabbit_plugins:read_enabled(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), - action_change(Opts, Node, ImplicitlyEnabled, ImplicitlyEnabled). +action(sync, Node, [], _Opts, PluginsFile, _PluginsDir) -> + sync(Node, true, PluginsFile). %%---------------------------------------------------------------------------- @@ -312,21 +313,23 @@ write_enabled_plugins(PluginsFile, Plugins) -> PluginsFile, Reason}}) end. -action_change(Opts, Node, Old, New) -> - action_change0(proplists:get_bool(?OFFLINE_OPT, Opts), Node, Old, New). +action_change(Opts, Node, Old, New, PluginsFile) -> + action_change0(proplists:get_bool(?OFFLINE_OPT, Opts), + proplists:get_bool(?ONLINE_OPT, Opts), + Node, Old, New, PluginsFile). -action_change0(true, _Node, Same, Same) -> +action_change0(true, _Online, _Node, Same, Same, _PluginsFile) -> %% Definitely nothing to do ok; -action_change0(true, _Node, _Old, _New) -> +action_change0(true, _Online, _Node, _Old, _New, _PluginsFile) -> io:format("Offline change; changes will take effect at broker restart.~n"); -action_change0(false, Node, _Old, New) -> - %% Don't care what the Old was in the plugins file, that might not - %% match what the server is running - so tell it to ensure we are - %% running the right apps even if "nothing has changed". - rpc_call(Node, rabbit_plugins, ensure, [New]). +action_change0(false, Online, Node, _Old, _New, PluginsFile) -> + sync(Node, Online, PluginsFile). -rpc_call(Node, Mod, Fun, Args) -> +sync(Node, ForceOnline, PluginsFile) -> + rpc_call(Node, ForceOnline, rabbit_plugins, ensure, [PluginsFile]). + +rpc_call(Node, Online, Mod, Fun, Args) -> io:format("Applying plugin configuration to ~s...", [Node]), case rpc:call(Node, Mod, Fun, Args) of {ok, [], []} -> @@ -338,6 +341,19 @@ rpc_call(Node, Mod, Fun, Args) -> {ok, Start, Stop} -> io:format(" stopped ~b plugin~s and started ~b plugin~s.~n", [length(Stop), plur(Stop), length(Start), plur(Start)]); + {badrpc, nodedown} = Error -> + io:format(" failed.~n", []), + case Online of + true -> Error; + false -> io:format( + " * Could not contact node ~s.~n" + " * Changes will take effect at broker restart.~n" + " * Specify --online for diagnostics and to treat " + "this as a failure.~n" + " * Specify --offline to disable changes to running " + "broker.~n", + [Node]) + end; {badrpc, _} = Error -> io:format(" failed.~n", []), Error -- cgit v1.2.1 From 7950556ca268a12f6d9cd747c568761d1e9958a4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 10 Jun 2014 16:41:55 +0100 Subject: Init the stats timer again on force_event_refresh, so that if stats have become enabled we do the right thing. --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_channel.erl | 2 +- src/rabbit_reader.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 753d8e15..97206df3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1165,7 +1165,7 @@ handle_cast({force_event_refresh, Ref}, emit_consumer_created( Ch, CTag, true, AckRequired, QName, Prefetch, Args, Ref) end, - noreply(State); + noreply(rabbit_event:init_stats_timer(State, #q.stats_timer)); handle_cast(notify_decorators, State) -> notify_decorators(State), diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 74f9cacf..15944270 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -341,7 +341,7 @@ handle_cast({send_drained, CTagCredit}, State = #ch{writer_pid = WriterPid}) -> handle_cast({force_event_refresh, Ref}, State) -> rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State), Ref), - noreply(State); + noreply(rabbit_event:init_stats_timer(State, #ch.stats_timer)); handle_cast({mandatory_received, MsgSeqNo}, State = #ch{mandatory = Mand}) -> %% NB: don't call noreply/1 since we don't want to send confirms. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ddaf205e..906c4b6e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -410,7 +410,7 @@ handle_other({'$gen_cast', {force_event_refresh, Ref}}, State) rabbit_event:notify( connection_created, [{type, network} | infos(?CREATION_EVENT_KEYS, State)], Ref), - State; + rabbit_event:init_stats_timer(State, #v1.stats_timer); handle_other({'$gen_cast', force_event_refresh}, State) -> %% Ignore, we will emit a created event once we start running. State; -- cgit v1.2.1 From 7f07114a267a6ec9792f69ec889c8f18666b6603 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 10 Jun 2014 17:53:31 +0100 Subject: Oops. --- src/rabbit_plugins.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 81f8c274..7817626c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -52,8 +52,8 @@ ensure(FileJustChanged) -> %% We need sync_notify here since mgmt will attempt to look at all %% the modules for the disabled plugins - if they are unloaded %% that won't work. - ok = rabbit_event:notify(plugins_changed, [{enabled, Start}, - {disabled, Stop}]), + ok = rabbit_event:sync_notify(plugins_changed, [{enabled, Start}, + {disabled, Stop}]), rabbit:stop_apps(Stop), clean_plugins(Stop), {ok, Start, Stop}; -- cgit v1.2.1 From 93923c729f9611ccc25912354efaaeffa55feed4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 11 Jun 2014 11:15:38 +0100 Subject: Report errors from the server (such as mismatched plugin files) correctly. --- src/rabbit_plugins_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index b7bc9ce6..a1243b77 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -354,7 +354,7 @@ rpc_call(Node, Online, Mod, Fun, Args) -> "broker.~n", [Node]) end; - {badrpc, _} = Error -> + Error -> io:format(" failed.~n", []), Error end. -- cgit v1.2.1 From f52e46697c016767e504f49693500df8393d897f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 11 Jun 2014 11:22:57 +0100 Subject: Show a warning if list fails to contact a node. --- src/rabbit_plugins_main.erl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index a1243b77..98418d8c 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -221,10 +221,11 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> AllEnabled = rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins), EnabledImplicitly = AllEnabled -- EnabledExplicitly, - Running = case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of - {badrpc, _} -> []; - Active -> Active - end, + {StatusMsg, Running} = + case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of + {badrpc, _} -> {"[failed to contact ~s - status not shown]", []}; + Active -> {"* = running on ~s", Active} + end, Missing = [#plugin{name = Name, dependencies = []} || Name <- ((EnabledExplicitly ++ EnabledImplicitly) -- plugin_names(AvailablePlugins))], @@ -244,8 +245,8 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> minimal -> ok; _ -> io:format(" Configured: E = explicitly enabled; " "e = implicitly enabled; ! = missing~n" - " | Status: * = running on ~s~n" - " |/~n", [Node]) + " | Status: ~s~n" + " |/~n", [rabbit_misc:format(StatusMsg, [Node])]) end, [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Running, plugin_names(Missing), Format, MaxWidth) || P <- Plugins1], -- cgit v1.2.1 From 3500f926f08349b5b13c4266e2d21c56f8010354 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 11 Jun 2014 12:50:32 +0100 Subject: A few last clenaups --- src/rabbit.erl | 16 +++++----------- src/rabbit_version.erl | 7 +++---- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 90181619..4901ea17 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -405,9 +405,7 @@ handle_app_error(Term) -> end. run_cleanup_steps(Apps) -> - [run_step(Name, Attributes, cleanup) || - {App, Name, Attributes} <- find_steps(Apps), - lists:member(App, Apps)], + [run_step(Name, Attrs, cleanup) || {_, Name, Attrs} <- find_steps(Apps)], ok. await_startup() -> @@ -525,16 +523,12 @@ run_boot_steps() -> run_boot_steps([App || {App, _, _} <- application:loaded_applications()]). run_boot_steps(Apps) -> - Steps = find_steps(Apps), - [ok = run_step(StepName, Attributes, mfa) || - {_, StepName, Attributes} <- Steps], + [ok = run_step(Step, Attrs, mfa) || {_, Step, Attrs} <- find_steps(Apps)], ok. -find_steps(BaseApps) -> - Apps = BaseApps -- [App || {App, _, _} <- rabbit_misc:which_applications()], - FullBoot = sort_boot_steps( - rabbit_misc:all_module_attributes(rabbit_boot_step)), - [Step || {App, _, _} = Step <- FullBoot, lists:member(App, Apps)]. +find_steps(Apps) -> + All = sort_boot_steps(rabbit_misc:all_module_attributes(rabbit_boot_step)), + [Step || {App, _, _} = Step <- All, lists:member(App, Apps)]. run_step(StepName, Attributes, AttributeName) -> case [MFA || {Key, MFA} <- Attributes, diff --git a/src/rabbit_version.erl b/src/rabbit_version.erl index ef480ccb..3a041508 100644 --- a/src/rabbit_version.erl +++ b/src/rabbit_version.erl @@ -113,11 +113,10 @@ upgrades_required(Scope) -> %% ------------------------------------------------------------------- with_upgrade_graph(Fun, Scope) -> - Attrs = rabbit_misc:all_module_attributes(rabbit_upgrade), case rabbit_misc:build_acyclic_graph( - fun ({Module, Steps}) -> vertices(Module, Steps, Scope) end, - fun ({Module, Steps}) -> edges(Module, Steps, Scope) end, - [{Mod, Steps} || {_, Mod, Steps} <- Attrs]) of + fun ({_App, Module, Steps}) -> vertices(Module, Steps, Scope) end, + fun ({_App, Module, Steps}) -> edges(Module, Steps, Scope) end, + rabbit_misc:all_module_attributes(rabbit_upgrade)) of {ok, G} -> try Fun(G) after -- cgit v1.2.1 From a2709e025032de4b910b76887e2162d3fb75c6e2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 11 Jun 2014 13:08:48 +0100 Subject: Move these down (ish) from federation. --- src/rabbit_amqqueue.erl | 10 +++++++++- src/rabbit_exchange.erl | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 7a26ddec..8a1d162a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -18,7 +18,7 @@ -export([recover/0, stop/0, start/1, declare/5, declare/6, delete_immediately/1, delete/3, purge/1, forget_all_durable/1]). --export([pseudo_queue/2]). +-export([pseudo_queue/2, immutable/1]). -export([lookup/1, not_found_or_absent/1, with/2, with/3, with_or_die/2, assert_equivalence/5, check_exclusive_access/2, with_exclusive_access_or_die/3, @@ -176,6 +176,7 @@ -spec(set_maximum_since_use/2 :: (pid(), non_neg_integer()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). -spec(pseudo_queue/2 :: (name(), pid()) -> rabbit_types:amqqueue()). +-spec(immutable/1 :: (rabbit_types:amqqueue()) -> rabbit_types:amqqueue()). -spec(store_queue/1 :: (rabbit_types:amqqueue()) -> 'ok'). -spec(update_decorators/1 :: (name()) -> 'ok'). -spec(policy_changed/2 :: @@ -723,6 +724,13 @@ pseudo_queue(QueueName, Pid) -> pid = Pid, slave_pids = []}. +immutable(Q) -> Q#amqqueue{pid = none, + slave_pids = none, + sync_slave_pids = none, + gm_pids = none, + policy = none, + decorators = none}. + deliver([], _Delivery, _Flow) -> %% /dev/null optimisation []; diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 2e0566b7..a1772f0a 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -21,7 +21,7 @@ -export([recover/0, policy_changed/2, callback/4, declare/6, assert_equivalence/6, assert_args_equivalence/2, check_type/1, lookup/1, lookup_or_die/1, list/0, list/1, lookup_scratch/2, - update_scratch/3, update_decorators/1, + update_scratch/3, update_decorators/1, immutable/1, info_keys/0, info/1, info/2, info_all/1, info_all/2, route/2, delete/2, validate_binding/2]). %% these must be run inside a mnesia tx @@ -73,6 +73,7 @@ fun((rabbit_types:exchange()) -> rabbit_types:exchange())) -> not_found | rabbit_types:exchange()). -spec(update_decorators/1 :: (name()) -> 'ok'). +-spec(immutable/1 :: (rabbit_types:exchange()) -> rabbit_types:exchange()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). -spec(info/2 :: @@ -307,6 +308,10 @@ update(Name, Fun) -> [] -> not_found end. +immutable(X) -> X#exchange{scratches = none, + policy = none, + decorators = none}. + info_keys() -> ?INFO_KEYS. map(VHostPath, F) -> -- cgit v1.2.1