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 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 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 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 From e488e880996be457068b84f9954ece05d08425aa Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 16 Jun 2014 10:39:13 +0400 Subject: Don't stat() queue when it is declared with nowait = true --- src/rabbit_channel.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 15944270..d35ad1a6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1046,9 +1046,14 @@ handle_method(#'queue.declare'{queue = QueueNameBin, _, State = #ch{virtual_host = VHostPath, conn_pid = ConnPid}) -> QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), + F = case NoWait of + false -> + fun (Q) -> {rabbit_amqqueue:stat(Q), Q} end; + true -> + fun (Q) -> {{ok, 0, 0}, Q} end + end, {{ok, MessageCount, ConsumerCount}, #amqqueue{} = Q} = - rabbit_amqqueue:with_or_die( - QueueName, fun (Q) -> {rabbit_amqqueue:stat(Q), Q} end), + rabbit_amqqueue:with_or_die(QueueName, F), ok = rabbit_amqqueue:check_exclusive_access(Q, ConnPid), return_queue_declare_ok(QueueName, NoWait, MessageCount, ConsumerCount, State); -- cgit v1.2.1 From 7e2a03c67acdb627a5e64bef16e9df88e1d21a64 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 16 Jun 2014 11:53:39 +0100 Subject: Update docs post bug 24926. --- packaging/common/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/common/README b/packaging/common/README index 0a29ee27..35a1523a 100644 --- a/packaging/common/README +++ b/packaging/common/README @@ -17,4 +17,4 @@ run as the superuser. An example configuration file is provided in the same directory as this README. Copy it to /etc/rabbitmq/rabbitmq.config to use it. The RabbitMQ server must be restarted after changing the configuration -file or enabling or disabling plugins. +file. -- cgit v1.2.1 From e0a5225cb21c47986e2546b67e344393bea6a519 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 16 Jun 2014 19:08:00 +0400 Subject: Cover passive = false case --- src/rabbit_channel.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d35ad1a6..ee863a39 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -992,7 +992,12 @@ handle_method(#'queue.declare'{queue = QueueNameBin, QueueName, fun (Q) -> ok = rabbit_amqqueue:assert_equivalence( Q, Durable, AutoDelete, Args, Owner), - rabbit_amqqueue:stat(Q) + case NoWait of + false -> + rabbit_amqqueue:stat(Q); + _ -> + {ok, 0, 0} + end end) of {ok, MessageCount, ConsumerCount} -> return_queue_declare_ok(QueueName, NoWait, MessageCount, -- cgit v1.2.1 From 243130acc943601ec3ab8bf79d7bfb41fa6a96d0 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 06:07:24 +0400 Subject: Log connection name instead of pid for channel errors --- src/rabbit_channel.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 15944270..1fabf10a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -437,13 +437,14 @@ handle_exception(Reason, State = #ch{protocol = Protocol, channel = Channel, writer_pid = WriterPid, reader_pid = ReaderPid, - conn_pid = ConnPid}) -> + conn_name = ConnName + }) -> %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("connection ~p, channel ~p - soft error:~n~p~n", - [ConnPid, Channel, Reason]), + rabbit_log:error("connection ~s, channel ~p - soft error:~n~p~n", + [ConnName, Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; {0, _} -> -- cgit v1.2.1 From bd3d1606d9a3beaa1fef2ddd0cc2705e9f4a8b01 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 06:25:11 +0400 Subject: Include vhost and username into channel error log messages --- src/rabbit_channel.erl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1fabf10a..db6aa989 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -437,14 +437,20 @@ handle_exception(Reason, State = #ch{protocol = Protocol, channel = Channel, writer_pid = WriterPid, reader_pid = ReaderPid, - conn_name = ConnName + conn_name = ConnName, + virtual_host = VHost, + user = #user{ + username = Username + } }) -> %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("connection ~s, channel ~p - soft error:~n~p~n", - [ConnName, Channel, Reason]), + rabbit_log:error("vhost ~s, user ~s, connection ~s, channel ~p - soft error:~n~p~n", + [binary_to_list(VHost), + binary_to_list(Username), + ConnName, Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; {0, _} -> -- cgit v1.2.1 From 8fa446b55cae79a13f0111d0e9a17f4833188a79 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 08:12:13 +0400 Subject: Include vhost into trace message headers --- src/rabbit_trace.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index aafd81df..017d1ab2 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -55,14 +55,17 @@ enabled(VHost) -> lists:member(VHost, VHosts). tap_in(_Msg, none) -> ok; -tap_in(Msg = #basic_message{exchange_name = #resource{name = XName}}, TraceX) -> - trace(TraceX, Msg, <<"publish">>, XName, []). +tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, + virtual_host = VHost}}, TraceX) -> + trace(TraceX, Msg, <<"publish">>, XName, + [{<<"vhost">>, longstr, VHost}]). tap_out(_Msg, none) -> ok; -tap_out({#resource{name = QName}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> +tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, - [{<<"redelivered">>, signedint, RedeliveredNum}]). + [{<<"redelivered">>, signedint, RedeliveredNum}, + {<<"vhost">>, longstr, VHost}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 917918ee264a88b36bb665239ea20ffc5344cc08 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 08:12:13 +0400 Subject: Include vhost into trace message headers --- src/rabbit_trace.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index aafd81df..017d1ab2 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -55,14 +55,17 @@ enabled(VHost) -> lists:member(VHost, VHosts). tap_in(_Msg, none) -> ok; -tap_in(Msg = #basic_message{exchange_name = #resource{name = XName}}, TraceX) -> - trace(TraceX, Msg, <<"publish">>, XName, []). +tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, + virtual_host = VHost}}, TraceX) -> + trace(TraceX, Msg, <<"publish">>, XName, + [{<<"vhost">>, longstr, VHost}]). tap_out(_Msg, none) -> ok; -tap_out({#resource{name = QName}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> +tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, - [{<<"redelivered">>, signedint, RedeliveredNum}]). + [{<<"redelivered">>, signedint, RedeliveredNum}, + {<<"vhost">>, longstr, VHost}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From d0fcec69d77836c6a3661ca5d2212461469a8c74 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 08:40:37 +0400 Subject: Include username into trace message headers --- src/rabbit_channel.erl | 10 ++++++---- src/rabbit_trace.erl | 22 +++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index db6aa989..9661c2e3 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -676,7 +676,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Content, State = #ch{virtual_host = VHostPath, tx = Tx, confirm_enabled = ConfirmEnabled, - trace_state = TraceState}) -> + trace_state = TraceState, + user = #user{username = Username}}) -> check_msg_size(Content), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), @@ -697,7 +698,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> - rabbit_trace:tap_in(Message, TraceState), + rabbit_trace:tap_in(Message, Username, TraceState), Delivery = rabbit_basic:delivery( Mandatory, DoConfirm, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), @@ -1372,7 +1373,8 @@ record_sent(ConsumerTag, AckRequired, Msg = {QName, QPid, MsgId, Redelivered, _Message}, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, - trace_state = TraceState}) -> + trace_state = TraceState, + user = #user{username = Username}}) -> ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of {none, true} -> get; {none, false} -> get_no_ack; @@ -1383,7 +1385,7 @@ record_sent(ConsumerTag, AckRequired, true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); false -> ok end, - rabbit_trace:tap_out(Msg, TraceState), + rabbit_trace:tap_out(Msg, Username, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, UAMQ); diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 017d1ab2..afb149d0 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -16,7 +16,7 @@ -module(rabbit_trace). --export([init/1, enabled/1, tap_in/2, tap_out/2, start/1, stop/1]). +-export([init/1, enabled/1, tap_in/3, tap_out/3, start/1, stop/1]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -32,8 +32,8 @@ -spec(init/1 :: (rabbit_types:vhost()) -> state()). -spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_in/2 :: (rabbit_types:basic_message(), state()) -> 'ok'). --spec(tap_out/2 :: (rabbit_amqqueue:qmsg(), state()) -> 'ok'). +-spec(tap_in/3 :: (rabbit_types:basic_message(), rabbit_types:username(), state()) -> 'ok'). +-spec(tap_out/3 :: (rabbit_amqqueue:qmsg(), rabbit_types:username(), state()) -> 'ok'). -spec(start/1 :: (rabbit_types:vhost()) -> 'ok'). -spec(stop/1 :: (rabbit_types:vhost()) -> 'ok'). @@ -54,18 +54,22 @@ enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). -tap_in(_Msg, none) -> ok; +tap_in(_Msg, _Username, none) -> ok; tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, - virtual_host = VHost}}, TraceX) -> + virtual_host = VHost}}, + Username, TraceX) -> trace(TraceX, Msg, <<"publish">>, XName, - [{<<"vhost">>, longstr, VHost}]). + [{<<"vhost">>, longstr, VHost}, + {<<"user">>, longstr, Username}]). -tap_out(_Msg, none) -> ok; -tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> +tap_out(_Msg, _Username, none) -> ok; +tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, + Username, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, [{<<"redelivered">>, signedint, RedeliveredNum}, - {<<"vhost">>, longstr, VHost}]). + {<<"vhost">>, longstr, VHost}, + {<<"user">>, longstr, Username}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 6452f057299ab69f7dd0257f79b7144979db38cc Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 08:40:37 +0400 Subject: Include username into trace message headers --- src/rabbit_channel.erl | 10 ++++++---- src/rabbit_trace.erl | 22 +++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 15944270..1b4ba7f1 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -669,7 +669,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Content, State = #ch{virtual_host = VHostPath, tx = Tx, confirm_enabled = ConfirmEnabled, - trace_state = TraceState}) -> + trace_state = TraceState, + user = #user{username = Username}}) -> check_msg_size(Content), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), @@ -690,7 +691,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> - rabbit_trace:tap_in(Message, TraceState), + rabbit_trace:tap_in(Message, Username, TraceState), Delivery = rabbit_basic:delivery( Mandatory, DoConfirm, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), @@ -1365,7 +1366,8 @@ record_sent(ConsumerTag, AckRequired, Msg = {QName, QPid, MsgId, Redelivered, _Message}, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, - trace_state = TraceState}) -> + trace_state = TraceState, + user = #user{username = Username}}) -> ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of {none, true} -> get; {none, false} -> get_no_ack; @@ -1376,7 +1378,7 @@ record_sent(ConsumerTag, AckRequired, true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); false -> ok end, - rabbit_trace:tap_out(Msg, TraceState), + rabbit_trace:tap_out(Msg, Username, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, UAMQ); diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 017d1ab2..afb149d0 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -16,7 +16,7 @@ -module(rabbit_trace). --export([init/1, enabled/1, tap_in/2, tap_out/2, start/1, stop/1]). +-export([init/1, enabled/1, tap_in/3, tap_out/3, start/1, stop/1]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -32,8 +32,8 @@ -spec(init/1 :: (rabbit_types:vhost()) -> state()). -spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_in/2 :: (rabbit_types:basic_message(), state()) -> 'ok'). --spec(tap_out/2 :: (rabbit_amqqueue:qmsg(), state()) -> 'ok'). +-spec(tap_in/3 :: (rabbit_types:basic_message(), rabbit_types:username(), state()) -> 'ok'). +-spec(tap_out/3 :: (rabbit_amqqueue:qmsg(), rabbit_types:username(), state()) -> 'ok'). -spec(start/1 :: (rabbit_types:vhost()) -> 'ok'). -spec(stop/1 :: (rabbit_types:vhost()) -> 'ok'). @@ -54,18 +54,22 @@ enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). -tap_in(_Msg, none) -> ok; +tap_in(_Msg, _Username, none) -> ok; tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, - virtual_host = VHost}}, TraceX) -> + virtual_host = VHost}}, + Username, TraceX) -> trace(TraceX, Msg, <<"publish">>, XName, - [{<<"vhost">>, longstr, VHost}]). + [{<<"vhost">>, longstr, VHost}, + {<<"user">>, longstr, Username}]). -tap_out(_Msg, none) -> ok; -tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> +tap_out(_Msg, _Username, none) -> ok; +tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, + Username, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, [{<<"redelivered">>, signedint, RedeliveredNum}, - {<<"vhost">>, longstr, VHost}]). + {<<"vhost">>, longstr, VHost}, + {<<"user">>, longstr, Username}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 2497e5a61ec7813c2e22542fe46403328c11e05d Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 09:34:38 +0400 Subject: backout bd87cb0026c5 (wrong branch) --- src/rabbit_trace.erl | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index afb149d0..53823b20 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -54,22 +54,15 @@ enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). -tap_in(_Msg, _Username, none) -> ok; -tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, - virtual_host = VHost}}, - Username, TraceX) -> - trace(TraceX, Msg, <<"publish">>, XName, - [{<<"vhost">>, longstr, VHost}, - {<<"user">>, longstr, Username}]). - -tap_out(_Msg, _Username, none) -> ok; -tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, - Username, TraceX) -> +tap_in(_Msg, none) -> ok; +tap_in(Msg = #basic_message{exchange_name = #resource{name = XName}}, TraceX) -> + trace(TraceX, Msg, <<"publish">>, XName, []). + +tap_out(_Msg, none) -> ok; +tap_out({#resource{name = QName}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, - [{<<"redelivered">>, signedint, RedeliveredNum}, - {<<"vhost">>, longstr, VHost}, - {<<"user">>, longstr, Username}]). + [{<<"redelivered">>, signedint, RedeliveredNum}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 5a943a9764be0f02edc21484560aaa07cb431c67 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 09:35:46 +0400 Subject: backout d57075513277 (wrong branch) --- src/rabbit_channel.erl | 10 ++++------ src/rabbit_trace.erl | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 9661c2e3..db6aa989 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -676,8 +676,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Content, State = #ch{virtual_host = VHostPath, tx = Tx, confirm_enabled = ConfirmEnabled, - trace_state = TraceState, - user = #user{username = Username}}) -> + trace_state = TraceState}) -> check_msg_size(Content), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), @@ -698,7 +697,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> - rabbit_trace:tap_in(Message, Username, TraceState), + rabbit_trace:tap_in(Message, TraceState), Delivery = rabbit_basic:delivery( Mandatory, DoConfirm, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), @@ -1373,8 +1372,7 @@ record_sent(ConsumerTag, AckRequired, Msg = {QName, QPid, MsgId, Redelivered, _Message}, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, - trace_state = TraceState, - user = #user{username = Username}}) -> + trace_state = TraceState}) -> ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of {none, true} -> get; {none, false} -> get_no_ack; @@ -1385,7 +1383,7 @@ record_sent(ConsumerTag, AckRequired, true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); false -> ok end, - rabbit_trace:tap_out(Msg, Username, TraceState), + rabbit_trace:tap_out(Msg, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, UAMQ); diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 53823b20..aafd81df 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -16,7 +16,7 @@ -module(rabbit_trace). --export([init/1, enabled/1, tap_in/3, tap_out/3, start/1, stop/1]). +-export([init/1, enabled/1, tap_in/2, tap_out/2, start/1, stop/1]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -32,8 +32,8 @@ -spec(init/1 :: (rabbit_types:vhost()) -> state()). -spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_in/3 :: (rabbit_types:basic_message(), rabbit_types:username(), state()) -> 'ok'). --spec(tap_out/3 :: (rabbit_amqqueue:qmsg(), rabbit_types:username(), state()) -> 'ok'). +-spec(tap_in/2 :: (rabbit_types:basic_message(), state()) -> 'ok'). +-spec(tap_out/2 :: (rabbit_amqqueue:qmsg(), state()) -> 'ok'). -spec(start/1 :: (rabbit_types:vhost()) -> 'ok'). -spec(stop/1 :: (rabbit_types:vhost()) -> 'ok'). -- cgit v1.2.1 From 5a939f21b7e0f450cb40c7791de356fd756057e1 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 10:04:07 +0400 Subject: Include connection name into trace message headers --- src/rabbit_channel.erl | 10 ++++++---- src/rabbit_trace.erl | 18 +++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1b4ba7f1..ac989d62 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -670,7 +670,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, tx = Tx, confirm_enabled = ConfirmEnabled, trace_state = TraceState, - user = #user{username = Username}}) -> + user = #user{username = Username}, + conn_name = ConnName}) -> check_msg_size(Content), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), @@ -691,7 +692,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> - rabbit_trace:tap_in(Message, Username, TraceState), + rabbit_trace:tap_in(Message, ConnName, Username, TraceState), Delivery = rabbit_basic:delivery( Mandatory, DoConfirm, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), @@ -1367,7 +1368,8 @@ record_sent(ConsumerTag, AckRequired, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, trace_state = TraceState, - user = #user{username = Username}}) -> + user = #user{username = Username}, + conn_name = ConnName}) -> ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of {none, true} -> get; {none, false} -> get_no_ack; @@ -1378,7 +1380,7 @@ record_sent(ConsumerTag, AckRequired, true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); false -> ok end, - rabbit_trace:tap_out(Msg, Username, TraceState), + rabbit_trace:tap_out(Msg, ConnName, Username, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, UAMQ); diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index afb149d0..8959eefb 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -16,7 +16,7 @@ -module(rabbit_trace). --export([init/1, enabled/1, tap_in/3, tap_out/3, start/1, stop/1]). +-export([init/1, enabled/1, tap_in/4, tap_out/4, start/1, stop/1]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -32,8 +32,10 @@ -spec(init/1 :: (rabbit_types:vhost()) -> state()). -spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_in/3 :: (rabbit_types:basic_message(), rabbit_types:username(), state()) -> 'ok'). --spec(tap_out/3 :: (rabbit_amqqueue:qmsg(), rabbit_types:username(), state()) -> 'ok'). +-spec(tap_in/4 :: (rabbit_types:basic_message(), string(), + rabbit_types:username(), state()) -> 'ok'). +-spec(tap_out/4 :: (rabbit_amqqueue:qmsg(), string(), + rabbit_types:username(), state()) -> 'ok'). -spec(start/1 :: (rabbit_types:vhost()) -> 'ok'). -spec(stop/1 :: (rabbit_types:vhost()) -> 'ok'). @@ -54,21 +56,23 @@ enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). -tap_in(_Msg, _Username, none) -> ok; +tap_in(_Msg, _ConnName, _Username, none) -> ok; tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, virtual_host = VHost}}, - Username, TraceX) -> + ConnName, Username, TraceX) -> trace(TraceX, Msg, <<"publish">>, XName, [{<<"vhost">>, longstr, VHost}, + {<<"connection">>, longstr, ConnName}, {<<"user">>, longstr, Username}]). -tap_out(_Msg, _Username, none) -> ok; +tap_out(_Msg, _ConnName, _Username, none) -> ok; tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, - Username, TraceX) -> + ConnName, Username, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, [{<<"redelivered">>, signedint, RedeliveredNum}, {<<"vhost">>, longstr, VHost}, + {<<"connection">>, longstr, ConnName}, {<<"user">>, longstr, Username}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 07eaccc11cf894f0bb1dae38d119bd31454ddc1a Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 10:23:13 +0400 Subject: Make mnesia:wait_for_tables timeout configurable --- ebin/rabbit_app.in | 1 + src/rabbit_table.erl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 7360208a..3647c04a 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -39,6 +39,7 @@ {server_properties, []}, {collect_statistics, none}, {collect_statistics_interval, 5000}, + {mnesia_table_loading_timeout, 30000}, {auth_mechanisms, ['PLAIN', 'AMQPLAIN']}, {auth_backends, [rabbit_auth_backend_internal]}, {delegate_count, 16}, diff --git a/src/rabbit_table.erl b/src/rabbit_table.erl index da75932d..dee252e3 100644 --- a/src/rabbit_table.erl +++ b/src/rabbit_table.erl @@ -70,7 +70,8 @@ wait_for_replicated() -> not lists:member({local_content, true}, TabDef)]). wait(TableNames) -> - case mnesia:wait_for_tables(TableNames, 30000) of + Timeout = application:get_env(rabbit, mnesia_table_loading_timeout), + case mnesia:wait_for_tables(TableNames, Timeout) of ok -> ok; {timeout, BadTabs} -> -- cgit v1.2.1 From 2a199550a574816a1892f17abcdf58d2183adc27 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 10:41:10 +0400 Subject: Mention mnesia_table_loading_timeout in rabbitmq.config.example --- docs/rabbitmq.config.example | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index 26de71b7..4fad1542 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -213,7 +213,12 @@ %% Explicitly enable/disable hipe compilation. %% - %% {hipe_compile, true} + %% {hipe_compile, true}, + + %% Timeout used when waiting for Mnesia tables in a cluster to + %% become available. + %% + %% {mnesia_table_loading_timeout, 30000} ]}, -- cgit v1.2.1 From a6a11354334fdd2524b4a5c0704fc77d87d07b41 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 11:16:03 +0400 Subject: Re-format based on feedback from Matthias --- src/rabbit_channel.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index db6aa989..da51bf0f 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -447,10 +447,11 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("vhost ~s, user ~s, connection ~s, channel ~p - soft error:~n~p~n", - [binary_to_list(VHost), + rabbit_log:error("connection ~s, channel ~p - soft error (vhost '~s', user '~s'):~n~p~n", + [ConnName, Channel, + binary_to_list(VHost), binary_to_list(Username), - ConnName, Channel, Reason]), + Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; {0, _} -> -- cgit v1.2.1 From 12b698dd77131f9e1f177f8bf83cccfa586b5c40 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 12:06:48 +0400 Subject: Oops --- src/rabbit_table.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_table.erl b/src/rabbit_table.erl index dee252e3..47c77cd0 100644 --- a/src/rabbit_table.erl +++ b/src/rabbit_table.erl @@ -70,7 +70,7 @@ wait_for_replicated() -> not lists:member({local_content, true}, TabDef)]). wait(TableNames) -> - Timeout = application:get_env(rabbit, mnesia_table_loading_timeout), + {ok, Timeout} = application:get_env(rabbit, mnesia_table_loading_timeout), case mnesia:wait_for_tables(TableNames, Timeout) of ok -> ok; -- cgit v1.2.1 From 175b162cf4052686fba96c97f362fdcd5385fc4c Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 12:35:08 +0400 Subject: Fetch SSL handshake timeout from app environment --- src/rabbit_networking.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 9082dbd3..96448f32 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -37,8 +37,6 @@ -include("rabbit.hrl"). -include_lib("kernel/include/inet.hrl"). --define(SSL_TIMEOUT, 5). %% seconds - -define(FIRST_TEST_BIND_PORT, 10000). %%---------------------------------------------------------------------------- @@ -168,9 +166,14 @@ ensure_ssl() -> end end. +ssl_timeout() -> + {ok, Val} = application:get_env(rabbit, ssl_handshake_timeout), + Val. + ssl_transform_fun(SslOpts) -> fun (Sock) -> - case catch ssl:ssl_accept(Sock, SslOpts, ?SSL_TIMEOUT * 1000) of + Timeout = ssl_timeout(), + case catch ssl:ssl_accept(Sock, SslOpts, Timeout) of {ok, SslSock} -> {ok, #ssl_socket{tcp = Sock, ssl = SslSock}}; {error, timeout} -> @@ -185,7 +188,7 @@ ssl_transform_fun(SslOpts) -> %% form, according to the TLS spec). So we give %% the ssl_connection a little bit of time to send %% such alerts. - timer:sleep(?SSL_TIMEOUT * 1000), + timer:sleep(Timeout), {error, {ssl_upgrade_error, Reason}}; {'EXIT', Reason} -> {error, {ssl_upgrade_failure, Reason}} -- cgit v1.2.1 From 02b09fe1678ca776bfc0a083cedc0408b78ec401 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 12:35:19 +0400 Subject: Add default for ssl_handshake_timeout --- ebin/rabbit_app.in | 1 + 1 file changed, 1 insertion(+) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 3647c04a..c1a86499 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -46,6 +46,7 @@ {trace_vhosts, []}, {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, + {ssl_handshake_timeout, 5000}, {reverse_dns_lookups, false}, {cluster_partition_handling, ignore}, {tcp_listen_options, [binary, -- cgit v1.2.1 From e8581f70b15beb9d2fadebd316fd3a3f2e0220dc Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 17 Jun 2014 12:35:33 +0400 Subject: Add ssl_handshake_timeout to rabbitmq.config.example --- docs/rabbitmq.config.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index 4fad1542..61823c80 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -103,6 +103,10 @@ %% %% {ssl_cert_login_from, common_name}, + %% SSL handshake timeout, in milliseconds. + %% + %% {ssl_handshake_timeout, 5000}, + %% %% Default User / VHost %% ==================== -- cgit v1.2.1 From 2b3729c6aeb3305de25a03c0b062c62df62450aa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 17 Jun 2014 10:31:12 +0100 Subject: Simplify. --- src/rabbit_channel.erl | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ee863a39..bb626554 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -992,12 +992,7 @@ handle_method(#'queue.declare'{queue = QueueNameBin, QueueName, fun (Q) -> ok = rabbit_amqqueue:assert_equivalence( Q, Durable, AutoDelete, Args, Owner), - case NoWait of - false -> - rabbit_amqqueue:stat(Q); - _ -> - {ok, 0, 0} - end + maybe_stat(NoWait, Q) end) of {ok, MessageCount, ConsumerCount} -> return_queue_declare_ok(QueueName, NoWait, MessageCount, @@ -1051,14 +1046,9 @@ handle_method(#'queue.declare'{queue = QueueNameBin, _, State = #ch{virtual_host = VHostPath, conn_pid = ConnPid}) -> QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), - F = case NoWait of - false -> - fun (Q) -> {rabbit_amqqueue:stat(Q), Q} end; - true -> - fun (Q) -> {{ok, 0, 0}, Q} end - end, {{ok, MessageCount, ConsumerCount}, #amqqueue{} = Q} = - rabbit_amqqueue:with_or_die(QueueName, F), + rabbit_amqqueue:with_or_die( + QueueName, fun (Q) -> {maybe_stat(NoWait, Q), Q} end), ok = rabbit_amqqueue:check_exclusive_access(Q, ConnPid), return_queue_declare_ok(QueueName, NoWait, MessageCount, ConsumerCount, State); @@ -1214,6 +1204,9 @@ basic_consume(QueueName, NoAck, ConsumerPrefetch, ActualConsumerTag, E end. +maybe_stat(false, Q) -> rabbit_amqqueue:stat(Q); +maybe_stat(true, _Q) -> {ok, 0, 0}. + consumer_monitor(ConsumerTag, State = #ch{consumer_mapping = ConsumerMapping, queue_monitors = QMons, -- cgit v1.2.1 From f7e6f21ade8e84a1a0cfdf50e4cf87221521a2fb Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 18 Jun 2014 05:04:01 +0400 Subject: Make [AMQP] connection timeout configurable --- ebin/rabbit_app.in | 1 + src/rabbit_reader.erl | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index c1a86499..f26e0f77 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -47,6 +47,7 @@ {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, {ssl_handshake_timeout, 5000}, + {handshake_timeout, 10000}, {reverse_dns_lookups, false}, {cluster_partition_handling, ignore}, {tcp_listen_options, [binary, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 906c4b6e..2d46dd54 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -27,7 +27,6 @@ -export([conserve_resources/3, server_properties/1]). --define(HANDSHAKE_TIMEOUT, 10). -define(NORMAL_TIMEOUT, 3). -define(CLOSING_TIMEOUT, 30). -define(CHANNEL_TERMINATION_TIMEOUT, 3). @@ -216,8 +215,9 @@ start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> exit(normal) end, log(info, "accepting AMQP connection ~p (~s)~n", [self(), Name]), + {ok, HandshakeTimeout} = application:get_env(rabbit, handshake_timeout), ClientSock = socket_op(Sock, SockTransform), - erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), handshake_timeout), + erlang:send_after(HandshakeTimeout, self(), handshake_timeout), {PeerHost, PeerPort, Host, Port} = socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end), ?store_proc_name(list_to_binary(Name)), @@ -231,7 +231,7 @@ start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> peer_port = PeerPort, protocol = none, user = none, - timeout_sec = ?HANDSHAKE_TIMEOUT, + timeout_sec = (HandshakeTimeout / 1000), frame_max = ?FRAME_MIN_SIZE, vhost = none, client_properties = none, -- cgit v1.2.1 From 847e18caaff7c10aa3d4237d52ccf05734ceb24b Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 18 Jun 2014 05:04:14 +0400 Subject: Add handshake_timeout to rabbitmq.config.example --- docs/rabbitmq.config.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index 61823c80..162af015 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -27,6 +27,10 @@ %% %% {ssl_listeners, [5671]}, + %% Complete connection handshake timeout, in milliseconds. + %% + %% {ssl_handshake_timeout, 10000}, + %% Log levels (currently just used for connection logging). %% One of 'info', 'warning', 'error' or 'none', in decreasing order %% of verbosity. Defaults to 'info'. -- cgit v1.2.1 From 0e41edd1e9d88b385e33a75b647e6d8fb6b76754 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jun 2014 15:19:47 +0100 Subject: Give sensible error messages when plugins are missing. --- src/rabbit_plugins.erl | 29 ++++++++++++++--------------- src/rabbit_plugins_main.erl | 44 +++++++++++++++++++------------------------- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 7817626c..9acaa1d4 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -130,19 +130,25 @@ 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. +%% Make sure we don't list OTP apps in here, and also that we detect +%% 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. + {OTP, Missing} = lists:partition(fun is_loadable/1, lists:usort(NotThere)), + case Missing of + [] -> ok; + _ -> Blame = [Name || #plugin{name = Name, + dependencies = Deps} <- Plugins, + lists:any(fun (Dep) -> + lists:member(Dep, Missing) + end, Deps)], + throw({error, {missing_dependencies, Missing, Blame}}) + end, + [P#plugin{dependencies = Deps -- OTP} + || P = #plugin{dependencies = Deps} <- Plugins]. is_loadable(App) -> case application:load(App) of @@ -162,13 +168,6 @@ prepare_plugins(Enabled) -> ToUnpack = dependencies(false, Enabled, AllPlugins), ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), - case Enabled -- plugin_names(ToUnpackPlugins) of - [] -> ok; - Missing -> error_logger:warning_msg( - "The following enabled plugins were not found: ~p~n", - [Missing]) - end, - case filelib:ensure_dir(ExpandDir ++ "/") of ok -> ok; {error, E2} -> throw({error, {cannot_create_plugins_expand_dir, diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 98418d8c..a3f12c96 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -86,6 +86,10 @@ start() -> {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> PrintInvalidCommandError(), usage(); + {error, {missing_dependencies, Missing, Blame}} -> + print_error("dependent plugins ~p not found; used by ~p.", + [Missing, Blame]), + rabbit_misc:quit(2); {error, Reason} -> print_error("~p", [Reason]), rabbit_misc:quit(2); @@ -137,18 +141,13 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), + case Missing of + [] -> ok; + _ -> throw({error_string, fmt_missing(Missing)}) + end, NewEnabled = lists:usort(Enabled ++ ToEnable), NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), - MissingDeps = (NewImplicitlyEnabled -- plugin_names(AllPlugins)) -- Missing, - case {Missing, MissingDeps} of - {[], []} -> ok; - {Miss, []} -> throw({error_string, fmt_missing("plugins", Miss)}); - {[], Miss} -> throw({error_string, fmt_missing("dependencies", Miss)}); - {_, _} -> throw({error_string, - fmt_missing("plugins", Missing) ++ - fmt_missing("dependencies", MissingDeps)}) - end, write_enabled_plugins(PluginsFile, NewEnabled), case NewEnabled -- ImplicitlyEnabled of [] -> io:format("Plugin configuration unchanged.~n"); @@ -226,12 +225,9 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> {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))], {ok, RE} = re:compile(Pattern), Plugins = [ Plugin || - Plugin = #plugin{name = Name} <- AvailablePlugins ++ Missing, + Plugin = #plugin{name = Name} <- AvailablePlugins, re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, if OnlyEnabled -> lists:member(Name, EnabledExplicitly); OnlyEnabledAll -> lists:member(Name, EnabledExplicitly) or @@ -244,25 +240,23 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> case Format of minimal -> ok; _ -> io:format(" Configured: E = explicitly enabled; " - "e = implicitly enabled; ! = missing~n" + "e = implicitly enabled~n" " | Status: ~s~n" " |/~n", [rabbit_misc:format(StatusMsg, [Node])]) end, [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Running, - plugin_names(Missing), Format, MaxWidth) || P <- Plugins1], + Format, MaxWidth) || P <- Plugins1], ok. format_plugin(#plugin{name = Name, version = Version, description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Running, - Missing, Format, MaxWidth) -> + EnabledExplicitly, EnabledImplicitly, Running, Format, + MaxWidth) -> EnabledGlyph = 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, EnabledImplicitly)} of + {true, false} -> "E"; + {false, true} -> "e"; + _ -> " " end, RunningGlyph = case lists:member(Name, Running) of true -> "*"; @@ -292,8 +286,8 @@ fmt_list(Header, Plugins) -> lists:flatten( [Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]). -fmt_missing(Desc, Missing) -> - fmt_list("The following " ++ Desc ++ " could not be found:", Missing). +fmt_missing(Missing) -> + fmt_list("The following plugins could not be found:", Missing). usort_plugins(Plugins) -> lists:usort(fun plugins_cmp/2, Plugins). -- cgit v1.2.1 From e8b72084734d980b267ee41d39197436d4a0efe2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jun 2014 17:08:35 +0100 Subject: rabbitmq-plugins set. --- docs/rabbitmq-plugins.1.xml | 64 +++++++++++++++++++++++++++++++++------------ src/rabbit_plugins_main.erl | 22 ++++++++++++++++ 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index e891969f..fb5f8e27 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -63,6 +63,16 @@ enabled. Implicitly enabled plugins are automatically disabled again when they are no longer required. + + + The enable, disable and + set commands will update the plugins file 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 behaviour. + @@ -150,14 +160,7 @@ - Enables the specified plugins and all their - dependencies. This will update the enabled plugins file - 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. + Enables the specified plugins and all their dependencies. For example: @@ -188,14 +191,7 @@ - Disables the specified plugins and all their - dependencies. This will update the enabled plugins file - 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. + Disables the specified plugins and all their dependencies. For example: @@ -206,6 +202,42 @@ + + + set --offline --online plugin ... + + + + --offline + Just modify the enabled plugins file. + + + --online + Treat failure to connect to the running broker as fatal. + + + plugin + Zero or more plugins to enable. + + + + Enables the specified plugins and all their + dependencies. Unlike rabbitmq-plugins + enable this command ignores and overwrites any + existing plugin configuration. rabbitmq-plugins + set with no plugin arguments is a legal command + meaning "disable all plugins". + + + For example: + rabbitmq-plugins set rabbitmq_management + + This command enables the management + plugin and its dependencies and disables everything else. + + + + diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index a3f12c96..7e9c4d60 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -44,6 +44,7 @@ [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, {enable, [?OFFLINE_DEF, ?ONLINE_DEF]}, {disable, [?OFFLINE_DEF, ?ONLINE_DEF]}, + {set, [?OFFLINE_DEF, ?ONLINE_DEF]}, {sync, []}]). %%---------------------------------------------------------------------------- @@ -157,6 +158,27 @@ action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> action_change( Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); +action(set, Node, ToSet0, Opts, PluginsFile, PluginsDir) -> + ToSet = [list_to_atom(Name) || Name <- ToSet0], + AllPlugins = rabbit_plugins:list(PluginsDir), + Enabled = rabbit_plugins:read_enabled(PluginsFile), + ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), + Missing = ToSet -- plugin_names(AllPlugins), + case Missing of + [] -> ok; + _ -> throw({error_string, fmt_missing(Missing)}) + end, + NewImplicitlyEnabled = rabbit_plugins:dependencies(false, + ToSet, AllPlugins), + write_enabled_plugins(PluginsFile, ToSet), + case NewImplicitlyEnabled of + [] -> io:format("All plugins are now disabled.~n"); + _ -> print_list("The following plugins are now enabled:", + NewImplicitlyEnabled) + end, + action_change( + Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); + action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> case ToDisable0 of [] -> throw({error_string, "Not enough arguments for 'disable'"}); -- cgit v1.2.1 From 783ff5c48c7344d729cf19f92883a4fb777fbe56 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jun 2014 17:10:22 +0100 Subject: Maybe clearer. --- docs/rabbitmq-plugins.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index fb5f8e27..f7be2d29 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -224,7 +224,7 @@ Enables the specified plugins and all their dependencies. Unlike rabbitmq-plugins enable this command ignores and overwrites any - existing plugin configuration. rabbitmq-plugins + existing enabled plugins. rabbitmq-plugins set with no plugin arguments is a legal command meaning "disable all plugins". -- cgit v1.2.1 From 6809383c13429daa5f5e5c86503c7e8f49b3189c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jun 2014 17:23:53 +0100 Subject: Hopefully clearer. --- src/rabbit_plugins_main.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 7e9c4d60..278fcf98 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -347,7 +347,7 @@ 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]), + io:format("~nApplying plugin configuration to ~s...", [Node]), case rpc:call(Node, Mod, Fun, Args) of {ok, [], []} -> io:format(" nothing to do.~n", []); @@ -364,10 +364,10 @@ rpc_call(Node, Online, Mod, Fun, Args) -> 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 " + " Changes will take effect at broker restart.~n" + " * Options: --online - fail if broker cannot be " + "contacted.~n" + " --offline - do not try to contact " "broker.~n", [Node]) end; -- cgit v1.2.1 From fda548c1e4004797534f90b4bbe4f1b36424d451 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 18 Jun 2014 22:20:11 +0400 Subject: Mention connection pid in channel error messages --- src/rabbit_channel.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index fdae2d79..6937cbe3 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -437,6 +437,7 @@ handle_exception(Reason, State = #ch{protocol = Protocol, channel = Channel, writer_pid = WriterPid, reader_pid = ReaderPid, + conn_pid = ConnPid, conn_name = ConnName, virtual_host = VHost, user = #user{ @@ -447,8 +448,8 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("connection ~s, channel ~p - soft error (vhost '~s', user '~s'):~n~p~n", - [ConnName, Channel, + rabbit_log:error("Channel error on connection ~s (~p), channel ~p (vhost '~s', user '~s'):~n~p~n", + [ConnName, ConnPid, Channel, binary_to_list(VHost), binary_to_list(Username), Reason]), -- cgit v1.2.1 From ddcc00cc2e3e9c6a89d223e946faa20de9cc33cd Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 18 Jun 2014 23:13:32 +0400 Subject: Mention connection name, user, and vhost in connection error log messages --- src/rabbit_reader.erl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 906c4b6e..56ad5499 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -581,16 +581,26 @@ maybe_close(State) -> termination_kind(normal) -> controlled; termination_kind(_) -> uncontrolled. +log_hard_error(State = #v1{connection_state = CS, + connection = #connection{name = ConnName, + user = #user{ + username = Username + }, + vhost = VHost}}, + Channel, Reason) -> + log(error, + "Connection error on connection ~s (~p, ~p), channel ~p (vhost '~s', user '~s'):~n~p~n", + [ConnName, self(), CS, Channel, + binary_to_list(VHost), binary_to_list(Username), Reason]). + handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> - log(error, "AMQP connection ~p (~p), channel ~p - error:~n~p~n", - [self(), closed, Channel, Reason]), + log_hard_error(State, Channel, Reason), State; handle_exception(State = #v1{connection = #connection{protocol = Protocol}, connection_state = CS}, Channel, Reason) when ?IS_RUNNING(State) orelse CS =:= closing -> - log(error, "AMQP connection ~p (~p), channel ~p - error:~n~p~n", - [self(), CS, Channel, Reason]), + log_hard_error(State, Channel, Reason), {0, CloseMethod} = rabbit_binary_generator:map_exception(Channel, Reason, Protocol), State1 = close_connection(terminate_channels(State)), -- cgit v1.2.1 From f8806ea63cef8b5f2396fdf7316367d9d73f8e98 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 06:57:50 +0400 Subject: Move user and vhost closer to connection info --- src/rabbit_channel.erl | 8 +++----- src/rabbit_reader.erl | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 6937cbe3..db733434 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -448,11 +448,9 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("Channel error on connection ~s (~p), channel ~p (vhost '~s', user '~s'):~n~p~n", - [ConnName, ConnPid, Channel, - binary_to_list(VHost), - binary_to_list(Username), - Reason]), + rabbit_log:error("Channel error on connection ~s (vhost '~s', user '~s', pid: ~p), channel ~p:~n~p~n", + [ConnName, binary_to_list(VHost), binary_to_list(Username), + ConnPid, Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; {0, _} -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 56ad5499..89cebca8 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -589,9 +589,9 @@ log_hard_error(State = #v1{connection_state = CS, vhost = VHost}}, Channel, Reason) -> log(error, - "Connection error on connection ~s (~p, ~p), channel ~p (vhost '~s', user '~s'):~n~p~n", - [ConnName, self(), CS, Channel, - binary_to_list(VHost), binary_to_list(Username), Reason]). + "Connection error on connection ~s (state: ~p, vhost: '~s', user: '~s', pid: ~p), channel ~p:~n~p~n", + [ConnName, CS, binary_to_list(VHost), binary_to_list(Username), + self(), Channel, Reason]). handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> log_hard_error(State, Channel, Reason), -- cgit v1.2.1 From 4781adc18df89e12a3fb6a6e39e092de066162bd Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 10:00:44 +0400 Subject: Log # of schedulers and if kernel poll is enabled on start --- src/rabbit.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 4901ea17..152d7ca8 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -799,7 +799,9 @@ log_banner() -> {"cookie hash", rabbit_nodes:cookie_hash()}, {"log", log_location(kernel)}, {"sasl log", log_location(sasl)}, - {"database dir", rabbit_mnesia:dir()}], + {"database dir", rabbit_mnesia:dir()}, + {"kernel polling", erlang:system_info(kernel_poll)}, + {"schedulers", integer_to_list(erlang:system_info(schedulers))}], DescrLen = 1 + lists:max([length(K) || {K, _V} <- Settings]), Format = fun (K, V) -> rabbit_misc:format( -- cgit v1.2.1 From 3d2009008faaf9b1798db48c491e0ae113ec85a6 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 10:14:19 +0400 Subject: Log warnings if kernel poll is disabled or VM is running with just 1 scheduler --- src/rabbit.erl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index 152d7ca8..79e24c4a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -491,6 +491,8 @@ start(normal, []) -> true = register(rabbit, self()), print_banner(), log_banner(), + warn_if_kernel_poll_is_disabled(), + warn_if_only_one_scheduler(), run_boot_steps(), {ok, SupPid}; Error -> @@ -818,6 +820,24 @@ log_banner() -> end || S <- Settings]), error_logger:info_msg("~s", [Banner]). +warn_if_kernel_poll_is_disabled() -> + case erlang:system_info(kernel_poll) of + true -> + ok; + false -> + error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) is disabled. Throughput and CPU utilization may worsen.~n", []), + ok + end. + +warn_if_only_one_scheduler() -> + case erlang:system_info(schedulers) of + 1 -> + error_logger:warning_msg("Erlang VM is running with only one scheduler. It will not be able to use multiple CPU cores.~n", []), + ok; + _ -> + ok + end. + home_dir() -> case init:get_argument(home) of {ok, [[Home]]} -> Home; -- cgit v1.2.1 From 1fc42de09d8e5cc5d31ec8c0ded2bcec66f8aa95 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 10:15:48 +0400 Subject: Add async-threads to the info banner --- src/rabbit.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 79e24c4a..8a524b62 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -803,7 +803,8 @@ log_banner() -> {"sasl log", log_location(sasl)}, {"database dir", rabbit_mnesia:dir()}, {"kernel polling", erlang:system_info(kernel_poll)}, - {"schedulers", integer_to_list(erlang:system_info(schedulers))}], + {"schedulers", integer_to_list(erlang:system_info(schedulers))}, + {"I/O thread pool size", integer_to_list(erlang:system_info(thread_pool_size))}], DescrLen = 1 + lists:max([length(K) || {K, _V} <- Settings]), Format = fun (K, V) -> rabbit_misc:format( -- cgit v1.2.1 From 7b237b626aa7161080103c763863d3fda18b3c5c Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 10:16:52 +0400 Subject: Add SMP support to the info banner --- src/rabbit.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index 8a524b62..760fb90c 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -803,6 +803,7 @@ log_banner() -> {"sasl log", log_location(sasl)}, {"database dir", rabbit_mnesia:dir()}, {"kernel polling", erlang:system_info(kernel_poll)}, + {"SMP support", erlang:system_info(smp_support)}, {"schedulers", integer_to_list(erlang:system_info(schedulers))}, {"I/O thread pool size", integer_to_list(erlang:system_info(thread_pool_size))}], DescrLen = 1 + lists:max([length(K) || {K, _V} <- Settings]), -- cgit v1.2.1 From ab8bb37299daaaae14e89140b82ff9c8d561e887 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 10:23:36 +0400 Subject: Log a warning if running with < 8 async threads --- src/rabbit.erl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 760fb90c..836b48bc 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -493,6 +493,7 @@ start(normal, []) -> log_banner(), warn_if_kernel_poll_is_disabled(), warn_if_only_one_scheduler(), + warn_if_few_async_threads(), run_boot_steps(), {ok, SupPid}; Error -> @@ -827,19 +828,28 @@ warn_if_kernel_poll_is_disabled() -> true -> ok; false -> - error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) is disabled. Throughput and CPU utilization may worsen.~n", []), + error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) is disabled. Throughput and CPU utilization may worsen.~n"), ok end. warn_if_only_one_scheduler() -> case erlang:system_info(schedulers) of 1 -> - error_logger:warning_msg("Erlang VM is running with only one scheduler. It will not be able to use multiple CPU cores.~n", []), + error_logger:warning_msg("Erlang VM is running with only one scheduler. It will not be able to use multiple CPU cores.~n"), ok; _ -> ok end. +warn_if_few_async_threads() -> + AsyncThreads = erlang:system_info(thread_pool_size), + if AsyncThreads < 8 -> + error_logger:warning_msg("Erlang VM is running with ~s I/O threads, file I/O performance may worsen ~n", [integer_to_list(AsyncThreads)]), + ok; + true -> + ok + end. + home_dir() -> case init:get_argument(home) of {ok, [[Home]]} -> Home; -- cgit v1.2.1 From d774b6b0ba6aecb7e11b27733e25ae0e01f4ad27 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 15:36:22 +0400 Subject: Re-format to 80 characters wide --- src/rabbit_channel.erl | 10 +++++----- src/rabbit_reader.erl | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index db733434..8955c406 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -440,16 +440,16 @@ handle_exception(Reason, State = #ch{protocol = Protocol, conn_pid = ConnPid, conn_name = ConnName, virtual_host = VHost, - user = #user{ - username = Username - } + user = User }) -> %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("Channel error on connection ~s (vhost '~s', user '~s', pid: ~p), channel ~p:~n~p~n", - [ConnName, binary_to_list(VHost), binary_to_list(Username), + rabbit_log:error("Channel error on connection ~s (vhost '~s'," + " user '~s', pid: ~p), channel ~p:~n~p~n", + [ConnName, binary_to_list(VHost), + binary_to_list(User#user.username), ConnPid, Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 89cebca8..be554c64 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -582,15 +582,15 @@ termination_kind(normal) -> controlled; termination_kind(_) -> uncontrolled. log_hard_error(State = #v1{connection_state = CS, - connection = #connection{name = ConnName, - user = #user{ - username = Username - }, - vhost = VHost}}, + connection = #connection{name = ConnName, + user = User, + vhost = VHost}}, Channel, Reason) -> log(error, - "Connection error on connection ~s (state: ~p, vhost: '~s', user: '~s', pid: ~p), channel ~p:~n~p~n", - [ConnName, CS, binary_to_list(VHost), binary_to_list(Username), + "Connection error on connection ~s (state: ~p, vhost: '~s'," + " user: '~s', pid: ~p), channel ~p:~n~p~n", + [ConnName, CS, + binary_to_list(VHost), binary_to_list(User#user.username), self(), Channel, Reason]). handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> -- cgit v1.2.1 From f6ee413d7fc5d54624be1c1ea1aa37876398a139 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 15:45:13 +0400 Subject: Fix handshake_timeout docs in rabbitmq.config.example --- docs/rabbitmq.config.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index 162af015..3c132e7d 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -27,9 +27,9 @@ %% %% {ssl_listeners, [5671]}, - %% Complete connection handshake timeout, in milliseconds. + %% Complete AMQP 0-9-1 connection handshake timeout, in milliseconds. %% - %% {ssl_handshake_timeout, 10000}, + %% {handshake_timeout, 10000}, %% Log levels (currently just used for connection logging). %% One of 'info', 'warning', 'error' or 'none', in decreasing order -- cgit v1.2.1 From e3921446e626cde367a97116569d60af74fe7c96 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 16:00:48 +0400 Subject: Mention that it includes TLS connections --- docs/rabbitmq.config.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index 3c132e7d..0315eeb4 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -27,7 +27,8 @@ %% %% {ssl_listeners, [5671]}, - %% Complete AMQP 0-9-1 connection handshake timeout, in milliseconds. + %% Complete AMQP 0-9-1 connection handshake timeout (including TLS/SSL connections), + %% in milliseconds. %% %% {handshake_timeout, 10000}, -- cgit v1.2.1 From aa810e595f1448dc255733cc49bf92dc1b58f668 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 19 Jun 2014 16:56:02 +0400 Subject: Include channel into trace messages --- src/rabbit_channel.erl | 9 ++++++--- src/rabbit_trace.erl | 18 +++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ac989d62..a356f819 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -668,6 +668,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, mandatory = Mandatory}, Content, State = #ch{virtual_host = VHostPath, tx = Tx, + channel = ChannelNum, confirm_enabled = ConfirmEnabled, trace_state = TraceState, user = #user{username = Username}, @@ -692,7 +693,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> - rabbit_trace:tap_in(Message, ConnName, Username, TraceState), + rabbit_trace:tap_in(Message, ConnName, ChannelNum, + Username, TraceState), Delivery = rabbit_basic:delivery( Mandatory, DoConfirm, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), @@ -1369,7 +1371,8 @@ record_sent(ConsumerTag, AckRequired, next_tag = DeliveryTag, trace_state = TraceState, user = #user{username = Username}, - conn_name = ConnName}) -> + conn_name = ConnName, + channel = ChannelNum}) -> ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of {none, true} -> get; {none, false} -> get_no_ack; @@ -1380,7 +1383,7 @@ record_sent(ConsumerTag, AckRequired, true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); false -> ok end, - rabbit_trace:tap_out(Msg, ConnName, Username, TraceState), + rabbit_trace:tap_out(Msg, ConnName, ChannelNum, Username, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, UAMQ); diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 8959eefb..c574562f 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -16,7 +16,7 @@ -module(rabbit_trace). --export([init/1, enabled/1, tap_in/4, tap_out/4, start/1, stop/1]). +-export([init/1, enabled/1, tap_in/5, tap_out/5, start/1, stop/1]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -32,9 +32,11 @@ -spec(init/1 :: (rabbit_types:vhost()) -> state()). -spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_in/4 :: (rabbit_types:basic_message(), string(), +-spec(tap_in/5 :: (rabbit_types:basic_message(), string(), + rabbit_channel:channel_number(), rabbit_types:username(), state()) -> 'ok'). --spec(tap_out/4 :: (rabbit_amqqueue:qmsg(), string(), +-spec(tap_out/5 :: (rabbit_amqqueue:qmsg(), string(), + rabbit_channel:channel_number(), rabbit_types:username(), state()) -> 'ok'). -spec(start/1 :: (rabbit_types:vhost()) -> 'ok'). @@ -56,23 +58,25 @@ enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). -tap_in(_Msg, _ConnName, _Username, none) -> ok; +tap_in(_Msg, _ConnName, _ChannelNum, _Username, none) -> ok; tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, virtual_host = VHost}}, - ConnName, Username, TraceX) -> + ConnName, ChannelNum, Username, TraceX) -> trace(TraceX, Msg, <<"publish">>, XName, [{<<"vhost">>, longstr, VHost}, {<<"connection">>, longstr, ConnName}, + {<<"channel">>, signedint, ChannelNum}, {<<"user">>, longstr, Username}]). -tap_out(_Msg, _ConnName, _Username, none) -> ok; +tap_out(_Msg, _ConnName, _ChannelNum, _Username, none) -> ok; tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, - ConnName, Username, TraceX) -> + ConnName, ChannelNum, Username, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, [{<<"redelivered">>, signedint, RedeliveredNum}, {<<"vhost">>, longstr, VHost}, {<<"connection">>, longstr, ConnName}, + {<<"channel">>, signedint, ChannelNum}, {<<"user">>, longstr, Username}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 9688d8bb3fbe59f18eacc3c923d47bec1b47af3a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 19 Jun 2014 16:57:15 +0100 Subject: Maintain down_slave_nodes so we might have a clue where to look to recover from. --- include/rabbit.hrl | 1 + src/rabbit_amqqueue.erl | 30 +++++++++++++++++++++++------- src/rabbit_amqqueue_process.erl | 9 +++++++++ src/rabbit_mirror_queue_misc.erl | 32 +++++++++++++++++++++----------- src/rabbit_node_monitor.erl | 1 + src/rabbit_upgrade_functions.erl | 18 ++++++++++++++++++ 6 files changed, 73 insertions(+), 18 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index c1386803..7a40f9eb 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -52,6 +52,7 @@ arguments, %% immutable pid, %% durable (just so we know home node) slave_pids, sync_slave_pids, %% transient + down_slave_nodes, %% durable policy, %% durable, implicit update as above gm_pids, %% transient decorators}). %% transient, recalculated as above diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8a1d162a..7a10d239 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -29,7 +29,7 @@ -export([basic_get/4, basic_consume/10, basic_cancel/4, notify_decorators/1]). -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([on_node_up/1, on_node_down/1]). -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]). @@ -174,6 +174,7 @@ (fun ((atom(), A) -> {[rabbit_types:msg_id()], A}))) -> 'ok'). -spec(set_ram_duration_target/2 :: (pid(), number() | 'infinity') -> 'ok'). -spec(set_maximum_since_use/2 :: (pid(), non_neg_integer()) -> 'ok'). +-spec(on_node_up/1 :: (node()) -> '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()). @@ -689,6 +690,20 @@ stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring). sync_mirrors(QPid) -> delegate:call(QPid, sync_mirrors). cancel_sync_mirrors(QPid) -> delegate:call(QPid, cancel_sync_mirrors). +on_node_up(Node) -> + ok = rabbit_misc:execute_mnesia_transaction( + fun () -> + Qs = mnesia:match_object(rabbit_queue, + #amqqueue{_ = '_'}, write), + [case lists:member(Node, DSNs) of + true -> DSNs1 = DSNs -- [Node], + store_queue( + Q#amqqueue{down_slave_nodes = DSNs1}); + false -> ok + end || #amqqueue{down_slave_nodes = DSNs} = Q <- Qs], + ok + end). + on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = @@ -724,12 +739,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}. +immutable(Q) -> Q#amqqueue{pid = none, + slave_pids = none, + sync_slave_pids = none, + down_slave_nodes = none, + gm_pids = none, + policy = none, + decorators = none}. deliver([], _Delivery, _Flow) -> %% /dev/null optimisation diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 97206df3..4082c53d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -84,6 +84,7 @@ memory, slave_pids, synchronised_slave_pids, + down_slave_nodes, backing_queue_status, state ]). @@ -810,6 +811,14 @@ i(synchronised_slave_pids, #q{q = #amqqueue{name = Name}}) -> false -> ''; true -> SSPids end; +i(down_slave_nodes, #q{q = #amqqueue{name = Name, + durable = Durable}}) -> + {ok, Q = #amqqueue{down_slave_nodes = Nodes}} = + rabbit_amqqueue:lookup(Name), + case Durable andalso rabbit_mirror_queue_misc:is_mirrored(Q) of + false -> ''; + true -> Nodes + end; i(state, #q{status = running}) -> credit_flow:state(); i(state, #q{status = State}) -> State; i(backing_queue_status, #q{backing_queue_state = BQS, backing_queue = BQ}) -> diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 7aec1ac8..9e8c4a18 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -78,9 +78,10 @@ remove_from_queue(QueueName, Self, DeadGMPids) -> %% get here. case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; - [Q = #amqqueue { pid = QPid, - slave_pids = SPids, - gm_pids = GMPids }] -> + [Q = #amqqueue { pid = QPid, + slave_pids = SPids, + gm_pids = GMPids, + down_slave_nodes = DSNs}] -> {DeadGM, AliveGM} = lists:partition( fun ({GM, _}) -> lists:member(GM, DeadGMPids) @@ -89,6 +90,9 @@ remove_from_queue(QueueName, Self, DeadGMPids) -> AlivePids = [Pid || {_GM, Pid} <- AliveGM], Alive = [Pid || Pid <- [QPid | SPids], lists:member(Pid, AlivePids)], + DSNs1 = [node(Pid) || + Pid <- SPids, + not lists:member(Pid, AlivePids)] ++ DSNs, {QPid1, SPids1} = promote_slave(Alive), case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> @@ -97,9 +101,10 @@ remove_from_queue(QueueName, Self, DeadGMPids) -> %% Either master hasn't changed, so %% we're ok to update mnesia; or we have %% become the master. - Q1 = Q#amqqueue{pid = QPid1, - slave_pids = SPids1, - gm_pids = AliveGM}, + Q1 = Q#amqqueue{pid = QPid1, + slave_pids = SPids1, + gm_pids = AliveGM, + down_slave_nodes = DSNs1}, store_updated_slaves(Q1), %% If we add and remove nodes at the same time we %% might tell the old master we need to sync and @@ -109,8 +114,9 @@ remove_from_queue(QueueName, Self, DeadGMPids) -> _ -> %% Master has changed, and we're not it. %% [1]. - Q1 = Q#amqqueue{slave_pids = Alive, - gm_pids = AliveGM}, + Q1 = Q#amqqueue{slave_pids = Alive, + gm_pids = AliveGM, + down_slave_nodes = DSNs1}, store_updated_slaves(Q1) end, {ok, QPid1, DeadPids} @@ -239,12 +245,16 @@ log(Level, QName, Fmt, Args) -> rabbit_log:log(mirroring, Level, "Mirrored ~s: " ++ Fmt, [rabbit_misc:rs(QName) | Args]). -store_updated_slaves(Q = #amqqueue{slave_pids = SPids, - sync_slave_pids = SSPids}) -> +store_updated_slaves(Q = #amqqueue{pid = MPid, + slave_pids = SPids, + sync_slave_pids = SSPids, + down_slave_nodes = DSNs}) -> %% TODO now that we clear sync_slave_pids in rabbit_durable_queue, %% do we still need this filtering? SSPids1 = [SSPid || SSPid <- SSPids, lists:member(SSPid, SPids)], - Q1 = Q#amqqueue{sync_slave_pids = SSPids1}, + DSNs1 = DSNs -- [node(P) || P <- [MPid | SPids]], + Q1 = Q#amqqueue{sync_slave_pids = SSPids1, + down_slave_nodes = DSNs1}, ok = rabbit_amqqueue:store_queue(Q1), %% Wake it up so that we emit a stats event rabbit_amqqueue:notify_policy_changed(Q1), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 14961478..1c971c1d 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -415,6 +415,7 @@ ensure_ping_timer(State) -> State, #state.down_ping_timer, ?RABBIT_DOWN_PING_INTERVAL, ping_nodes). handle_live_rabbit(Node) -> + ok = rabbit_amqqueue:on_node_up(Node), ok = rabbit_alarm:on_node_up(Node), ok = rabbit_mnesia:on_node_up(Node). diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index b6d37852..1104f373 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -48,6 +48,7 @@ -rabbit_upgrade({queue_decorators, mnesia, [gm_pids]}). -rabbit_upgrade({internal_system_x, mnesia, [exchange_decorators]}). -rabbit_upgrade({cluster_name, mnesia, [runtime_parameters]}). +-rabbit_upgrade({down_slave_nodes, mnesia, [queue_decorators]}). %% ------------------------------------------------------------------- @@ -77,6 +78,8 @@ -spec(policy_apply_to/0 :: () -> 'ok'). -spec(queue_decorators/0 :: () -> 'ok'). -spec(internal_system_x/0 :: () -> 'ok'). +-spec(cluster_name/0 :: () -> 'ok'). +-spec(down_slave_nodes/0 :: () -> 'ok'). -endif. @@ -382,6 +385,21 @@ cluster_name_tx() -> [mnesia:delete(T, K, write) || K <- Ks], ok. +down_slave_nodes() -> + ok = down_slave_nodes(rabbit_queue), + ok = down_slave_nodes(rabbit_durable_queue). + +down_slave_nodes(Table) -> + transform( + Table, + fun ({amqqueue, Name, Durable, AutoDelete, ExclusiveOwner, Arguments, + Pid, SlavePids, SyncSlavePids, Policy, GmPids, Decorators}) -> + {amqqueue, Name, Durable, AutoDelete, ExclusiveOwner, Arguments, + Pid, SlavePids, SyncSlavePids, [], Policy, GmPids, Decorators} + end, + [name, durable, auto_delete, exclusive_owner, arguments, pid, slave_pids, + sync_slave_pids, down_slave_nodes, policy, gm_pids, decorators]). + %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> -- cgit v1.2.1 From 5b3520c23e98f08b72522de32c0846b4ff579e89 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 19 Jun 2014 17:59:47 +0100 Subject: Promote down slave to master if the master gets forgotten. --- src/rabbit_amqqueue.erl | 37 +++++++++++++++++++++++-------------- src/rabbit_misc.erl | 7 ++++++- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 7a10d239..4e23dbd2 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -258,15 +258,16 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> declare(QueueName, Durable, AutoDelete, Args, Owner, Node) -> ok = check_declare_arguments(QueueName, Args), 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 = []})), + rabbit_policy:set(#amqqueue{name = QueueName, + durable = Durable, + auto_delete = AutoDelete, + arguments = Args, + exclusive_owner = Owner, + pid = none, + slave_pids = [], + sync_slave_pids = [], + down_slave_nodes = [], + gm_pids = []})), Node = rabbit_mirror_queue_misc:initial_queue_node(Q, Node), gen_server2:call(start_queue_process(Node, Q), {init, new}, infinity). @@ -666,15 +667,23 @@ forget_all_durable(Node) -> fun () -> Qs = mnesia:match_object(rabbit_durable_queue, #amqqueue{_ = '_'}, write), - [rabbit_binding:process_deletions( - internal_delete1(Name)) || - #amqqueue{name = Name, pid = Pid} = Q <- Qs, - node(Pid) =:= Node, - rabbit_policy:get(<<"ha-mode">>, Q) =:= undefined], + [forget_node_for_queue(Q) || #amqqueue{pid = Pid} = Q <- Qs, + node(Pid) =:= Node], ok end), ok. +forget_node_for_queue(#amqqueue{name = Name, + down_slave_nodes = []}) -> + %% No slaves to recover from, queue is gone + rabbit_binding:process_deletions(internal_delete1(Name)); + +forget_node_for_queue(Q = #amqqueue{down_slave_nodes = [H|T]}) -> + %% Promote a slave while down - it'll happily recover as a master + Q1 = Q#amqqueue{pid = rabbit_misc:node_to_fake_pid(H), + down_slave_nodes = T}, + ok = mnesia:write(rabbit_durable_queue, Q1, write). + run_backing_queue(QPid, Mod, Fun) -> gen_server2:cast(QPid, {run_backing_queue, Mod, Fun}). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 6f353da5..fd4b7b11 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -45,7 +45,7 @@ -export([with_local_io/1, local_info_msg/2]). -export([unfold/2, ceil/1, queue_fold/3]). -export([sort_field_table/1]). --export([pid_to_string/1, string_to_pid/1]). +-export([pid_to_string/1, string_to_pid/1, node_to_fake_pid/1]). -export([version_compare/2, version_compare/3]). -export([version_minor_equivalent/2]). -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). @@ -193,6 +193,7 @@ (rabbit_framing:amqp_table()) -> rabbit_framing:amqp_table()). -spec(pid_to_string/1 :: (pid()) -> string()). -spec(string_to_pid/1 :: (string()) -> pid()). +-spec(node_to_fake_pid/1 :: (atom()) -> pid()). -spec(version_compare/2 :: (string(), string()) -> 'lt' | 'eq' | 'gt'). -spec(version_compare/3 :: (string(), string(), ('lt' | 'lte' | 'eq' | 'gte' | 'gt')) @@ -709,6 +710,10 @@ string_to_pid(Str) -> throw(Err) end. +%% node(node_to_fake_pid(Node)) =:= Node. +node_to_fake_pid(Node) -> + string_to_pid(format("<~s.0.0.0>", [Node])). + version_compare(A, B, lte) -> case version_compare(A, B) of eq -> true; -- cgit v1.2.1 From e4d4c41035ddd07ba62273e4212d7495f23ed405 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 02:54:07 +0400 Subject: More consistent connection formatting per suggestion from Matthias --- src/rabbit_channel.erl | 8 ++++---- src/rabbit_reader.erl | 34 ++++++++++++++++++++++------------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 8955c406..0c012898 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -446,11 +446,11 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("Channel error on connection ~s (vhost '~s'," - " user '~s', pid: ~p), channel ~p:~n~p~n", - [ConnName, binary_to_list(VHost), + rabbit_log:error("Channel error on connection ~p (~s, vhost: '~s'," + " user: '~s'), channel ~p:~n~p~n", + [ConnPid, ConnName, binary_to_list(VHost), binary_to_list(User#user.username), - ConnPid, Channel, Reason]), + Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; {0, _} -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index be554c64..0261a036 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -189,10 +189,10 @@ server_capabilities(_) -> log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args). socket_error(Reason) when is_atom(Reason) -> - log(error, "error on AMQP connection ~p: ~s~n", + log(error, "Error on AMQP connection ~p: ~s~n", [self(), rabbit_misc:format_inet_error(Reason)]); socket_error(Reason) -> - log(error, "error on AMQP connection ~p:~n~p~n", [self(), Reason]). + log(error, "Error on AMQP connection ~p:~n~p~n", [self(), Reason]). inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F). @@ -548,7 +548,12 @@ wait_for_channel_termination(0, TimerRef, State) -> end; _ -> State end; -wait_for_channel_termination(N, TimerRef, State) -> +wait_for_channel_termination(N, TimerRef, + State = #v1{connection_state = CS, + connection = #connection{ + name = ConnName, + user = User, + vhost = VHost}}) -> receive {'DOWN', _MRef, process, ChPid, Reason} -> {Channel, State1} = channel_cleanup(ChPid, State), @@ -558,9 +563,13 @@ wait_for_channel_termination(N, TimerRef, State) -> {_, controlled} -> wait_for_channel_termination( N-1, TimerRef, State1); {_, uncontrolled} -> log(error, - "AMQP connection ~p, channel ~p - " + "Error on AMQP connection ~p (~s, vhost: '~s'," + " user: '~s', state: ~p), channel ~p:" "error while terminating:~n~p~n", - [self(), Channel, Reason]), + [self(), ConnName, + binary_to_list(VHost), + binary_to_list(User#user.username), + CS, Channel, Reason]), wait_for_channel_termination( N-1, TimerRef, State1) end; @@ -582,16 +591,17 @@ termination_kind(normal) -> controlled; termination_kind(_) -> uncontrolled. log_hard_error(State = #v1{connection_state = CS, - connection = #connection{name = ConnName, - user = User, - vhost = VHost}}, + connection = #connection{ + name = ConnName, + user = User, + vhost = VHost}}, Channel, Reason) -> log(error, - "Connection error on connection ~s (state: ~p, vhost: '~s'," - " user: '~s', pid: ~p), channel ~p:~n~p~n", - [ConnName, CS, + "Error on AMQP connection ~p (~s, vhost: '~s'," + " user: '~s', state: ~p), channel ~p:~n~p~n", + [self(), ConnName, binary_to_list(VHost), binary_to_list(User#user.username), - self(), Channel, Reason]). + CS, Channel, Reason]). handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> log_hard_error(State, Channel, Reason), -- cgit v1.2.1 From f0f4497b37e9c42c8fd6d5d18f2d26557da0a7f2 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 11:44:47 +0400 Subject: Remove single scheduler warning It's not entirely clear what the heuristics should be. --- src/rabbit.erl | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 836b48bc..fe4c7214 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -201,6 +201,7 @@ %% practice 2 processes seems just as fast as any other number > 1, %% and keeps the progress bar realistic-ish. -define(HIPE_PROCESSES, 2). +-define(ASYNC_THREADS_WARNING_THRESHOLD, 8). %%---------------------------------------------------------------------------- @@ -492,7 +493,6 @@ start(normal, []) -> print_banner(), log_banner(), warn_if_kernel_poll_is_disabled(), - warn_if_only_one_scheduler(), warn_if_few_async_threads(), run_boot_steps(), {ok, SupPid}; @@ -832,18 +832,9 @@ warn_if_kernel_poll_is_disabled() -> ok end. -warn_if_only_one_scheduler() -> - case erlang:system_info(schedulers) of - 1 -> - error_logger:warning_msg("Erlang VM is running with only one scheduler. It will not be able to use multiple CPU cores.~n"), - ok; - _ -> - ok - end. - warn_if_few_async_threads() -> AsyncThreads = erlang:system_info(thread_pool_size), - if AsyncThreads < 8 -> + if AsyncThreads < ?ASYNC_THREADS_WARNING_THRESHOLD -> error_logger:warning_msg("Erlang VM is running with ~s I/O threads, file I/O performance may worsen ~n", [integer_to_list(AsyncThreads)]), ok; true -> -- cgit v1.2.1 From f9e2e89ca9ad761bb7e4242dcfc575a50639ee14 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 11:47:39 +0400 Subject: Don't log SMP support, async threads, etc --- src/rabbit.erl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index fe4c7214..e95fed53 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -802,11 +802,7 @@ log_banner() -> {"cookie hash", rabbit_nodes:cookie_hash()}, {"log", log_location(kernel)}, {"sasl log", log_location(sasl)}, - {"database dir", rabbit_mnesia:dir()}, - {"kernel polling", erlang:system_info(kernel_poll)}, - {"SMP support", erlang:system_info(smp_support)}, - {"schedulers", integer_to_list(erlang:system_info(schedulers))}, - {"I/O thread pool size", integer_to_list(erlang:system_info(thread_pool_size))}], + {"database dir", rabbit_mnesia:dir()}], DescrLen = 1 + lists:max([length(K) || {K, _V} <- Settings]), Format = fun (K, V) -> rabbit_misc:format( -- cgit v1.2.1 From 795c86f111f208c47d55df8d7b1865b52698b803 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 11:56:34 +0400 Subject: Formatting --- src/rabbit.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index e95fed53..b2df4191 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -824,14 +824,19 @@ warn_if_kernel_poll_is_disabled() -> true -> ok; false -> - error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) is disabled. Throughput and CPU utilization may worsen.~n"), + error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) " + "is disabled. Throughput and" + "CPU utilization may worsen.~n"), ok end. warn_if_few_async_threads() -> AsyncThreads = erlang:system_info(thread_pool_size), if AsyncThreads < ?ASYNC_THREADS_WARNING_THRESHOLD -> - error_logger:warning_msg("Erlang VM is running with ~s I/O threads, file I/O performance may worsen ~n", [integer_to_list(AsyncThreads)]), + error_logger:warning_msg( + "Erlang VM is running with ~s I/O threads, " + "file I/O performance may worsen ~n", + [integer_to_list(AsyncThreads)]), ok; true -> ok -- cgit v1.2.1 From 79abe9af7456b4ad6688fe9d1b1ab0e32c6185b5 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 12:00:40 +0400 Subject: Add RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS to VM arguments --- scripts/rabbitmq-server | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index bd397441..13411a5a 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -131,6 +131,7 @@ exec ${ERL_DIR}erl \ ${RABBITMQ_CONFIG_ARG} \ +W w \ ${RABBITMQ_SERVER_ERL_ARGS} \ + ${RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS} \ ${RABBITMQ_LISTEN_ARG} \ -sasl errlog_type error \ -sasl sasl_error_logger false \ -- cgit v1.2.1 From 929aab4d004cb2fbd0329c38745dc9f8dce138c5 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 12:13:32 +0400 Subject: Make sure RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS has effect on Windows --- scripts/rabbitmq-server.bat | 1 + scripts/rabbitmq-service.bat | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 043204fa..e2312406 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -147,6 +147,7 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( -kernel inet_default_connect_options "[{nodelay, true}]" ^ !RABBITMQ_LISTEN_ARG! ^ !RABBITMQ_SERVER_ERL_ARGS! ^ +!RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS! ^ -sasl errlog_type error ^ -sasl sasl_error_logger false ^ -rabbit error_logger {file,\""!LOGS:\=/!"\"} ^ diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 895561d4..fb2703f2 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -235,6 +235,7 @@ set ERLANG_SERVICE_ARGUMENTS= ^ -os_mon start_memsup false ^ -mnesia dir \""!RABBITMQ_MNESIA_DIR:\=/!"\" ^ !RABBITMQ_SERVER_START_ARGS! ^ +!RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS! ^ !RABBITMQ_DIST_ARG! ^ !STARVAR! -- cgit v1.2.1 From b4d20f4d07294e98aa1ea4261adc2ccd0be26a30 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 12:19:51 +0400 Subject: Cosmetics --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index b2df4191..38b19e75 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -826,7 +826,7 @@ warn_if_kernel_poll_is_disabled() -> false -> error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) " "is disabled. Throughput and" - "CPU utilization may worsen.~n"), + "CPU utilization may worsen.~n"), ok end. -- cgit v1.2.1 From b605fb50bde555dce1e8c9488e8e1efb171d8180 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 13:01:49 +0400 Subject: Warn if Nagle's algorithm is enabled by default --- src/rabbit.erl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 38b19e75..a1bd76aa 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -494,6 +494,7 @@ start(normal, []) -> log_banner(), warn_if_kernel_poll_is_disabled(), warn_if_few_async_threads(), + warn_if_nagles_algorithm_is_enabled(), run_boot_steps(), {ok, SupPid}; Error -> @@ -825,7 +826,7 @@ warn_if_kernel_poll_is_disabled() -> ok; false -> error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) " - "is disabled. Throughput and" + "is disabled. Throughput and " "CPU utilization may worsen.~n"), ok end. @@ -842,6 +843,21 @@ warn_if_few_async_threads() -> ok end. +warn_if_nagles_algorithm_is_enabled() -> + IDCOpts = application:get_env(kernel, inet_default_connect_options, []), + Msg = "Nagle's algorithm is enabled for sockets, " + "network I/O latency will be higher~n", + case proplists:lookup(nodelay, IDCOpts) of + none -> + error_logger:warning_msg(Msg), + ok; + {nodelay, false} -> + error_logger:warning_msg(Msg), + ok; + {nodelay, true} -> + ok + end. + home_dir() -> case init:get_argument(home) of {ok, [[Home]]} -> Home; -- cgit v1.2.1 From c405c8c5192c9cb129831d0aa992f557bd4ac887 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 13:30:49 +0400 Subject: Track connection timestamps --- src/rabbit_reader.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 906c4b6e..a45871fb 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -43,7 +43,7 @@ -record(connection, {name, host, peer_host, port, peer_port, protocol, user, timeout_sec, frame_max, channel_max, vhost, client_properties, capabilities, - auth_mechanism, auth_state}). + auth_mechanism, auth_state, connected_at}). -record(throttle, {alarmed_by, last_blocked_by, last_blocked_at}). @@ -237,7 +237,8 @@ start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> client_properties = none, capabilities = [], auth_mechanism = none, - auth_state = none}, + auth_state = none, + connected_at = os:timestamp()}, callback = uninitialized_callback, recv_len = 0, pending_recv = false, @@ -1129,6 +1130,7 @@ ic(channel_max, #connection{channel_max = ChMax}) -> ChMax; ic(client_properties, #connection{client_properties = CP}) -> CP; ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; +ic(connected_at, #connection{connected_at = Timestamp}) -> Timestamp; ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> -- cgit v1.2.1 From b5347bb8a8ac06514a191da000a788a9bd08f46c Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 14:10:09 +0400 Subject: Format connected_at info item --- src/rabbit_reader.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a45871fb..e1b4dbef 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1130,7 +1130,7 @@ ic(channel_max, #connection{channel_max = ChMax}) -> ChMax; ic(client_properties, #connection{client_properties = CP}) -> CP; ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; -ic(connected_at, #connection{connected_at = Timestamp}) -> Timestamp; +ic(connected_at, #connection{connected_at = T}) -> timestamp_ms(T); ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> @@ -1173,6 +1173,11 @@ emit_stats(State) -> _ -> State1 end. +timestamp_ms(unknown) -> + unknown; +timestamp_ms(Timestamp) -> + timer:now_diff(Timestamp, {0,0,0}) div 1000. + %% 1.0 stub -ifdef(use_specs). -spec(become_1_0/2 :: (non_neg_integer(), #v1{}) -> no_return()). -- cgit v1.2.1 From 7ef9e982688760c1b12107bd89dd5f6e330fee1b Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 20 Jun 2014 14:27:16 +0400 Subject: Add connected_at to the list of statistics keys --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e1b4dbef..fa272f3b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -48,7 +48,7 @@ -record(throttle, {alarmed_by, last_blocked_by, last_blocked_at}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, - send_pend, state, channels]). + send_pend, state, channels, connected_at]). -define(CREATION_EVENT_KEYS, [pid, name, port, peer_port, host, -- cgit v1.2.1 From 2e987359dfb14b82a203f95a682639a7dcc7b891 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 20 Jun 2014 11:30:43 +0100 Subject: Cosmetic. --- src/rabbit_channel.erl | 2 +- src/rabbit_trace.erl | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a356f819..4efee84a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -671,7 +671,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, channel = ChannelNum, confirm_enabled = ConfirmEnabled, trace_state = TraceState, - user = #user{username = Username}, + user = #user{username = Username}, conn_name = ConnName}) -> check_msg_size(Content), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index c574562f..f14caf37 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -59,25 +59,26 @@ enabled(VHost) -> lists:member(VHost, VHosts). tap_in(_Msg, _ConnName, _ChannelNum, _Username, none) -> ok; -tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, +tap_in(Msg = #basic_message{exchange_name = #resource{name = XName, virtual_host = VHost}}, ConnName, ChannelNum, Username, TraceX) -> trace(TraceX, Msg, <<"publish">>, XName, - [{<<"vhost">>, longstr, VHost}, - {<<"connection">>, longstr, ConnName}, - {<<"channel">>, signedint, ChannelNum}, - {<<"user">>, longstr, Username}]). + [{<<"vhost">>, longstr, VHost}, + {<<"connection">>, longstr, ConnName}, + {<<"channel">>, signedint, ChannelNum}, + {<<"user">>, longstr, Username}]). tap_out(_Msg, _ConnName, _ChannelNum, _Username, none) -> ok; -tap_out({#resource{name = QName, virtual_host = VHost}, _QPid, _QMsgId, Redelivered, Msg}, +tap_out({#resource{name = QName, virtual_host = VHost}, + _QPid, _QMsgId, Redelivered, Msg}, ConnName, ChannelNum, Username, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, trace(TraceX, Msg, <<"deliver">>, QName, [{<<"redelivered">>, signedint, RedeliveredNum}, - {<<"vhost">>, longstr, VHost}, - {<<"connection">>, longstr, ConnName}, - {<<"channel">>, signedint, ChannelNum}, - {<<"user">>, longstr, Username}]). + {<<"vhost">>, longstr, VHost}, + {<<"connection">>, longstr, ConnName}, + {<<"channel">>, signedint, ChannelNum}, + {<<"user">>, longstr, Username}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From b2516bb2844bbd9f151f10ff7c74b1b9b7145d31 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 14:44:04 +0400 Subject: No need to convert to lists here --- src/rabbit_reader.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 0261a036..1d96a042 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -567,8 +567,7 @@ wait_for_channel_termination(N, TimerRef, " user: '~s', state: ~p), channel ~p:" "error while terminating:~n~p~n", [self(), ConnName, - binary_to_list(VHost), - binary_to_list(User#user.username), + VHost, User#user.username, CS, Channel, Reason]), wait_for_channel_termination( N-1, TimerRef, State1) @@ -600,7 +599,7 @@ log_hard_error(State = #v1{connection_state = CS, "Error on AMQP connection ~p (~s, vhost: '~s'," " user: '~s', state: ~p), channel ~p:~n~p~n", [self(), ConnName, - binary_to_list(VHost), binary_to_list(User#user.username), + VHost, User#user.username, CS, Channel, Reason]). handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> -- cgit v1.2.1 From 2b7f95bf3b4ced64f6164138c886485ffbf36f11 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 14:45:45 +0400 Subject: Formatting --- src/rabbit_reader.erl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 1d96a042..a738888a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -558,19 +558,20 @@ wait_for_channel_termination(N, TimerRef, {'DOWN', _MRef, process, ChPid, Reason} -> {Channel, State1} = channel_cleanup(ChPid, State), case {Channel, termination_kind(Reason)} of - {undefined, _} -> exit({abnormal_dependent_exit, - ChPid, Reason}); - {_, controlled} -> wait_for_channel_termination( - N-1, TimerRef, State1); - {_, uncontrolled} -> log(error, - "Error on AMQP connection ~p (~s, vhost: '~s'," - " user: '~s', state: ~p), channel ~p:" - "error while terminating:~n~p~n", - [self(), ConnName, - VHost, User#user.username, - CS, Channel, Reason]), - wait_for_channel_termination( - N-1, TimerRef, State1) + {undefined, _} -> + exit({abnormal_dependent_exit, + ChPid, Reason}); + {_, controlled} -> + wait_for_channel_termination( + N-1, TimerRef, State1); + {_, uncontrolled} -> + log(error, "Error on AMQP connection ~p (~s, vhost: '~s'," + " user: '~s', state: ~p), channel ~p:" + "error while terminating:~n~p~n", + [self(), ConnName, VHost, User#user.username, + CS, Channel, Reason]), + wait_for_channel_termination( + N-1, TimerRef, State1) end; cancel_wait -> exit(channel_termination_timeout) -- cgit v1.2.1 From 8e9a63a39c5eece42fd30ee71ea1b684c2d1af4c Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 14:46:47 +0400 Subject: Eliminate a compiler warning --- src/rabbit_reader.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a738888a..61d15707 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -590,11 +590,11 @@ maybe_close(State) -> termination_kind(normal) -> controlled; termination_kind(_) -> uncontrolled. -log_hard_error(State = #v1{connection_state = CS, - connection = #connection{ - name = ConnName, - user = User, - vhost = VHost}}, +log_hard_error(#v1{connection_state = CS, + connection = #connection{ + name = ConnName, + user = User, + vhost = VHost}}, Channel, Reason) -> log(error, "Error on AMQP connection ~p (~s, vhost: '~s'," -- cgit v1.2.1 From 3b0f321781d8630499cbc8fb208042f5e82b3524 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 15:16:48 +0400 Subject: Wording --- docs/rabbitmq.config.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index 0315eeb4..a4438646 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -27,8 +27,8 @@ %% %% {ssl_listeners, [5671]}, - %% Complete AMQP 0-9-1 connection handshake timeout (including TLS/SSL connections), - %% in milliseconds. + %% Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection + %% and SSL handshake), in milliseconds. %% %% {handshake_timeout, 10000}, -- cgit v1.2.1 From 5e4b76702f8e84b05a4ce4eb361cf113e2506c0d Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 15:41:41 +0400 Subject: Accept SERVER_ADDITIONAL_ERL_ARGS from rabbitmq-env.conf --- scripts/rabbitmq-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 13411a5a..2dbda427 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -39,7 +39,7 @@ DEFAULT_NODE_PORT=5672 [ "x" = "x$RABBITMQ_LOG_BASE" ] && RABBITMQ_LOG_BASE=${LOG_BASE} [ "x" = "x$RABBITMQ_MNESIA_BASE" ] && RABBITMQ_MNESIA_BASE=${MNESIA_BASE} [ "x" = "x$RABBITMQ_SERVER_START_ARGS" ] && RABBITMQ_SERVER_START_ARGS=${SERVER_START_ARGS} - +[ "x" = "x$RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS" ] && RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=${SERVER_ADDITIONAL_ERL_ARGS} [ "x" = "x$RABBITMQ_MNESIA_DIR" ] && RABBITMQ_MNESIA_DIR=${MNESIA_DIR} [ "x" = "x$RABBITMQ_MNESIA_DIR" ] && RABBITMQ_MNESIA_DIR=${RABBITMQ_MNESIA_BASE}/${RABBITMQ_NODENAME} -- cgit v1.2.1 From 4d0bca608d414fb673384be2612384e4965150ca Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 15:50:04 +0400 Subject: error_logger:warning_msg itself returns ok --- src/rabbit.erl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index a1bd76aa..41b35b28 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -827,8 +827,7 @@ warn_if_kernel_poll_is_disabled() -> false -> error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) " "is disabled. Throughput and " - "CPU utilization may worsen.~n"), - ok + "CPU utilization may worsen.~n") end. warn_if_few_async_threads() -> @@ -837,8 +836,7 @@ warn_if_few_async_threads() -> error_logger:warning_msg( "Erlang VM is running with ~s I/O threads, " "file I/O performance may worsen ~n", - [integer_to_list(AsyncThreads)]), - ok; + [integer_to_list(AsyncThreads)]); true -> ok end. @@ -849,11 +847,9 @@ warn_if_nagles_algorithm_is_enabled() -> "network I/O latency will be higher~n", case proplists:lookup(nodelay, IDCOpts) of none -> - error_logger:warning_msg(Msg), - ok; + error_logger:warning_msg(Msg); {nodelay, false} -> - error_logger:warning_msg(Msg), - ok; + error_logger:warning_msg(Msg); {nodelay, true} -> ok end. -- cgit v1.2.1 From 3fe047bd71b25a3f89b0d11968a038b03ec58e31 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 22 Jun 2014 15:52:59 +0400 Subject: Use proplists:get_value/3 here --- src/rabbit.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 41b35b28..77bd451a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -845,12 +845,10 @@ warn_if_nagles_algorithm_is_enabled() -> IDCOpts = application:get_env(kernel, inet_default_connect_options, []), Msg = "Nagle's algorithm is enabled for sockets, " "network I/O latency will be higher~n", - case proplists:lookup(nodelay, IDCOpts) of - none -> - error_logger:warning_msg(Msg); - {nodelay, false} -> + case proplists:get_value(nodelay, IDCOpts, false) of + false -> error_logger:warning_msg(Msg); - {nodelay, true} -> + true -> ok end. -- cgit v1.2.1 From 7acecfc18ba08b864b7c32c5e4c98cea79d1c7b9 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 23 Jun 2014 09:36:45 +0400 Subject: Format connected_at to a human-readable date and time --- src/rabbit_reader.erl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index fa272f3b..8fbdef51 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -48,14 +48,14 @@ -record(throttle, {alarmed_by, last_blocked_by, last_blocked_at}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, - send_pend, state, channels, connected_at]). + send_pend, state, channels]). -define(CREATION_EVENT_KEYS, [pid, name, port, peer_port, host, peer_host, ssl, peer_cert_subject, peer_cert_issuer, peer_cert_validity, auth_mechanism, ssl_protocol, ssl_key_exchange, ssl_cipher, ssl_hash, protocol, user, vhost, - timeout, frame_max, channel_max, client_properties]). + timeout, frame_max, channel_max, client_properties, connected_at]). -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). @@ -1130,7 +1130,7 @@ ic(channel_max, #connection{channel_max = ChMax}) -> ChMax; ic(client_properties, #connection{client_properties = CP}) -> CP; ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; -ic(connected_at, #connection{connected_at = T}) -> timestamp_ms(T); +ic(connected_at, #connection{connected_at = T}) -> timestamp(T); ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> @@ -1173,10 +1173,17 @@ emit_stats(State) -> _ -> State1 end. -timestamp_ms(unknown) -> +timestamp(unknown) -> unknown; -timestamp_ms(Timestamp) -> - timer:now_diff(Timestamp, {0,0,0}) div 1000. +timestamp(Timestamp) -> + {{Y, M, D}, {H, Min, S}} = calendar:now_to_local_time(Timestamp), + print("~w-~2.2.0w-~2.2.0w ~w:~2.2.0w:~2.2.0w", [Y, M, D, H, Min, S]). + +print(Fmt, Val) when is_list(Val) -> + list_to_binary(lists:flatten(io_lib:format(Fmt, Val))); +print(Fmt, Val) -> + print(Fmt, [Val]). + %% 1.0 stub -ifdef(use_specs). -- cgit v1.2.1 From 815af3470a6ee9b05f0d1491767344a6eb1e848f Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 23 Jun 2014 09:37:16 +0400 Subject: Include connected_at into default keys displayed by `rabbitmqctl list_connections` --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 451f4d70..59ae7afc 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -423,7 +423,7 @@ action(list_bindings, Node, Args, Opts, Inform) -> action(list_connections, Node, Args, _Opts, Inform) -> Inform("Listing connections", []), - ArgAtoms = default_if_empty(Args, [user, peer_host, peer_port, state]), + ArgAtoms = default_if_empty(Args, [user, peer_host, peer_port, state, connected_at]), display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, [ArgAtoms]), ArgAtoms); -- cgit v1.2.1 From 292a9e59fd1d8dd6042b923fb6f915d94cb18a98 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 23 Jun 2014 10:05:44 +0400 Subject: List connected_at in rabbitmqctl usage --- docs/rabbitmqctl.1.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 01b024a2..7977f95e 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1475,6 +1475,10 @@ send_pend Send queue size. + + connected_at + Date and time this connection was established. + If no connectioninfoitems are -- cgit v1.2.1 From c195ba9ecfc8512d0a8d6deb6c334d8d9123f38f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 23 Jun 2014 13:23:08 +0100 Subject: Whitespace --- docs/rabbitmq.config.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example index a4438646..e8b56660 100644 --- a/docs/rabbitmq.config.example +++ b/docs/rabbitmq.config.example @@ -27,7 +27,7 @@ %% %% {ssl_listeners, [5671]}, - %% Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection + %% Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection %% and SSL handshake), in milliseconds. %% %% {handshake_timeout, 10000}, -- cgit v1.2.1 From a24d1dea67a07cf7f9fe172d5112d094311404f1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 23 Jun 2014 13:53:16 +0100 Subject: Cosmetic: these can all be rolled together, and be a bit terser. --- src/rabbit.erl | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 77bd451a..fda1fbdc 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -492,9 +492,7 @@ start(normal, []) -> true = register(rabbit, self()), print_banner(), log_banner(), - warn_if_kernel_poll_is_disabled(), - warn_if_few_async_threads(), - warn_if_nagles_algorithm_is_enabled(), + warn_if_kernel_config_dubious(), run_boot_steps(), {ok, SupPid}; Error -> @@ -820,36 +818,27 @@ log_banner() -> end || S <- Settings]), error_logger:info_msg("~s", [Banner]). -warn_if_kernel_poll_is_disabled() -> +warn_if_kernel_config_dubious() -> case erlang:system_info(kernel_poll) of - true -> - ok; - false -> - error_logger:warning_msg("Kernel poll (epoll, kqueue, etc) " - "is disabled. Throughput and " - "CPU utilization may worsen.~n") - end. - -warn_if_few_async_threads() -> + true -> ok; + false -> error_logger:warning_msg( + "Kernel poll (epoll, kqueue, etc) is disabled. Throughput " + "and CPU utilization may worsen.~n") + end, AsyncThreads = erlang:system_info(thread_pool_size), - if AsyncThreads < ?ASYNC_THREADS_WARNING_THRESHOLD -> - error_logger:warning_msg( - "Erlang VM is running with ~s I/O threads, " - "file I/O performance may worsen ~n", - [integer_to_list(AsyncThreads)]); - true -> - ok - end. - -warn_if_nagles_algorithm_is_enabled() -> + case AsyncThreads < ?ASYNC_THREADS_WARNING_THRESHOLD of + true -> error_logger:warning_msg( + "Erlang VM is running with ~s I/O threads, " + "file I/O performance may worsen ~n", + [integer_to_list(AsyncThreads)]); + false -> ok + end, IDCOpts = application:get_env(kernel, inet_default_connect_options, []), - Msg = "Nagle's algorithm is enabled for sockets, " - "network I/O latency will be higher~n", case proplists:get_value(nodelay, IDCOpts, false) of - false -> - error_logger:warning_msg(Msg); - true -> - ok + false -> error_logger:warning_msg( + "Nagle's algorithm is enabled for sockets, " + "network I/O latency will be higher~n"); + true -> ok end. home_dir() -> -- cgit v1.2.1 From fa89e9e575bd6b2ec5ebe447b1952121a4b79ae7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 23 Jun 2014 13:55:55 +0100 Subject: Format as number, don't explicitly convert to string then format as string. --- src/rabbit.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index fda1fbdc..6fe54f79 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -828,9 +828,8 @@ warn_if_kernel_config_dubious() -> AsyncThreads = erlang:system_info(thread_pool_size), case AsyncThreads < ?ASYNC_THREADS_WARNING_THRESHOLD of true -> error_logger:warning_msg( - "Erlang VM is running with ~s I/O threads, " - "file I/O performance may worsen ~n", - [integer_to_list(AsyncThreads)]); + "Erlang VM is running with ~b I/O threads, " + "file I/O performance may worsen~n", [AsyncThreads]); false -> ok end, IDCOpts = application:get_env(kernel, inet_default_connect_options, []), -- cgit v1.2.1 From 1b6acda56689df581e8a61f6578b0d40514a5dca Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 23 Jun 2014 17:15:56 +0400 Subject: Make rabbitmqctl list connected_at as a machine-readable timestamp --- src/rabbit_reader.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 8fbdef51..cd4c2af8 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1175,9 +1175,8 @@ emit_stats(State) -> timestamp(unknown) -> unknown; -timestamp(Timestamp) -> - {{Y, M, D}, {H, Min, S}} = calendar:now_to_local_time(Timestamp), - print("~w-~2.2.0w-~2.2.0w ~w:~2.2.0w:~2.2.0w", [Y, M, D, H, Min, S]). +timestamp({Mega, Sec, Micro}) -> + (Mega * 1000000 * 1000000 + Sec * 1000000 + Micro) div 1000. print(Fmt, Val) when is_list(Val) -> list_to_binary(lists:flatten(io_lib:format(Fmt, Val))); -- cgit v1.2.1 From e6576f2c5cca60d4c6cdf69b24923c4b8feddfa9 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 23 Jun 2014 17:20:07 +0400 Subject: Remove connected_at from the default list_connections key list --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 59ae7afc..451f4d70 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -423,7 +423,7 @@ action(list_bindings, Node, Args, Opts, Inform) -> action(list_connections, Node, Args, _Opts, Inform) -> Inform("Listing connections", []), - ArgAtoms = default_if_empty(Args, [user, peer_host, peer_port, state, connected_at]), + ArgAtoms = default_if_empty(Args, [user, peer_host, peer_port, state]), display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, [ArgAtoms]), ArgAtoms); -- cgit v1.2.1 From 05b4e285dab9621fe5c20977294052aa00542b02 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 23 Jun 2014 15:42:01 +0100 Subject: application:get_env/3 is not available in R13B03. --- src/rabbit.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 6fe54f79..4b7a9a1f 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -832,7 +832,10 @@ warn_if_kernel_config_dubious() -> "file I/O performance may worsen~n", [AsyncThreads]); false -> ok end, - IDCOpts = application:get_env(kernel, inet_default_connect_options, []), + IDCOpts = case application:get_env(kernel, inet_default_connect_options) of + undefined -> []; + {ok, Val} -> Val + end, case proplists:get_value(nodelay, IDCOpts, false) of false -> error_logger:warning_msg( "Nagle's algorithm is enabled for sockets, " -- cgit v1.2.1 From fc6737d5450fea892694ae25546cf791150207c2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 24 Jun 2014 16:17:30 +0100 Subject: Cosmetic: unwrap several lines. Remove some more binary_to_list/1. --- src/rabbit_channel.erl | 18 ++++++++---------- src/rabbit_reader.erl | 16 +++++----------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0c012898..8d5af9d7 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -433,23 +433,21 @@ send(_Command, #ch{state = closing}) -> send(Command, #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:send_command(WriterPid, Command). -handle_exception(Reason, State = #ch{protocol = Protocol, - channel = Channel, - writer_pid = WriterPid, - reader_pid = ReaderPid, - conn_pid = ConnPid, - conn_name = ConnName, +handle_exception(Reason, State = #ch{protocol = Protocol, + channel = Channel, + writer_pid = WriterPid, + reader_pid = ReaderPid, + conn_pid = ConnPid, + conn_name = ConnName, virtual_host = VHost, - user = User - }) -> + user = User}) -> %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> rabbit_log:error("Channel error on connection ~p (~s, vhost: '~s'," " user: '~s'), channel ~p:~n~p~n", - [ConnPid, ConnName, binary_to_list(VHost), - binary_to_list(User#user.username), + [ConnPid, ConnName, VHost, User#user.username, Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 61d15707..c121f10e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -559,19 +559,16 @@ wait_for_channel_termination(N, TimerRef, {Channel, State1} = channel_cleanup(ChPid, State), case {Channel, termination_kind(Reason)} of {undefined, _} -> - exit({abnormal_dependent_exit, - ChPid, Reason}); + exit({abnormal_dependent_exit, ChPid, Reason}); {_, controlled} -> - wait_for_channel_termination( - N-1, TimerRef, State1); + wait_for_channel_termination(N-1, TimerRef, State1); {_, uncontrolled} -> log(error, "Error on AMQP connection ~p (~s, vhost: '~s'," " user: '~s', state: ~p), channel ~p:" "error while terminating:~n~p~n", [self(), ConnName, VHost, User#user.username, CS, Channel, Reason]), - wait_for_channel_termination( - N-1, TimerRef, State1) + wait_for_channel_termination(N-1, TimerRef, State1) end; cancel_wait -> exit(channel_termination_timeout) @@ -594,14 +591,11 @@ log_hard_error(#v1{connection_state = CS, connection = #connection{ name = ConnName, user = User, - vhost = VHost}}, - Channel, Reason) -> + vhost = VHost}}, Channel, Reason) -> log(error, "Error on AMQP connection ~p (~s, vhost: '~s'," " user: '~s', state: ~p), channel ~p:~n~p~n", - [self(), ConnName, - VHost, User#user.username, - CS, Channel, Reason]). + [self(), ConnName, VHost, User#user.username, CS, Channel, Reason]). handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> log_hard_error(State, Channel, Reason), -- cgit v1.2.1 From ea99739bc3c9620107b045676ed9e5a703c5e197 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 24 Jun 2014 16:38:38 +0100 Subject: These are binaries --- src/rabbit_trace.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index f14caf37..dbc2856d 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -32,10 +32,10 @@ -spec(init/1 :: (rabbit_types:vhost()) -> state()). -spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_in/5 :: (rabbit_types:basic_message(), string(), +-spec(tap_in/5 :: (rabbit_types:basic_message(), binary(), rabbit_channel:channel_number(), rabbit_types:username(), state()) -> 'ok'). --spec(tap_out/5 :: (rabbit_amqqueue:qmsg(), string(), +-spec(tap_out/5 :: (rabbit_amqqueue:qmsg(), binary(), rabbit_channel:channel_number(), rabbit_types:username(), state()) -> 'ok'). -- cgit v1.2.1 From c8b118bae80c9cedc9fe3f6fdd20ef52ffade31c Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 09:29:03 +0400 Subject: print/2 is no longer used --- src/rabbit_reader.erl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index cd4c2af8..4dc3bc82 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1178,11 +1178,6 @@ timestamp(unknown) -> timestamp({Mega, Sec, Micro}) -> (Mega * 1000000 * 1000000 + Sec * 1000000 + Micro) div 1000. -print(Fmt, Val) when is_list(Val) -> - list_to_binary(lists:flatten(io_lib:format(Fmt, Val))); -print(Fmt, Val) -> - print(Fmt, [Val]). - %% 1.0 stub -ifdef(use_specs). -- cgit v1.2.1 From 1d13fcd5edbdddea2cdd0fd5b7916a838a72028a Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 09:30:02 +0400 Subject: connected_at is a timestamp now --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 7977f95e..fc055084 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1477,7 +1477,7 @@ connected_at - Date and time this connection was established. + Timestamp this connection was established. -- cgit v1.2.1 From 17538aaeab765a5dde9efc56cbc2ddbb6ddca6c2 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 09:30:37 +0400 Subject: Timestamp cannot be unknown --- src/rabbit_reader.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 4dc3bc82..7304ccf3 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1173,8 +1173,6 @@ emit_stats(State) -> _ -> State1 end. -timestamp(unknown) -> - unknown; timestamp({Mega, Sec, Micro}) -> (Mega * 1000000 * 1000000 + Sec * 1000000 + Micro) div 1000. -- cgit v1.2.1 From 4a624ea190e67ff4d49c1620311f35cfdf761f1d Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 10:47:13 +0400 Subject: Convert connected_at to timestamp when bound since it is immutable --- src/rabbit_reader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 7304ccf3..7c032893 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -238,7 +238,7 @@ start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> capabilities = [], auth_mechanism = none, auth_state = none, - connected_at = os:timestamp()}, + connected_at = timestamp(os:timestamp())}, callback = uninitialized_callback, recv_len = 0, pending_recv = false, @@ -1130,7 +1130,7 @@ ic(channel_max, #connection{channel_max = ChMax}) -> ChMax; ic(client_properties, #connection{client_properties = CP}) -> CP; ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; -ic(connected_at, #connection{connected_at = T}) -> timestamp(T); +ic(connected_at, #connection{connected_at = T}) -> T; ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> -- cgit v1.2.1 From a7566597d7d98de0f78cccb89380718d3d06a124 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 11:02:19 +0400 Subject: Wording --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index fc055084..6cfd3e00 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1477,7 +1477,7 @@ connected_at - Timestamp this connection was established. + Date and time this connection was established, as timestamp. -- cgit v1.2.1 From 1ae80591d93ba15300f3af73e14250ef8ea63ce8 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 16:02:32 +0400 Subject: Move timestamp formatting fn to rabbit_misc --- src/rabbit_misc.erl | 4 ++++ src/rabbit_reader.erl | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 6f353da5..286d8bbc 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -71,6 +71,7 @@ -export([get_parent/0]). -export([store_proc_name/1, store_proc_name/2]). -export([moving_average/4]). +-export([timestamp/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -1016,6 +1017,9 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. +timestamp({Mega, Sec, Micro}) -> + (Mega * 1000000 * 1000000 + Sec * 1000000 + Micro) div 1000. + check_expiry(N) when N < 0 -> {error, {value_negative, N}}; check_expiry(_N) -> ok. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 7c032893..ba62c7a4 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -238,7 +238,7 @@ start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> capabilities = [], auth_mechanism = none, auth_state = none, - connected_at = timestamp(os:timestamp())}, + connected_at = rabbit_misc:timestamp(os:timestamp())}, callback = uninitialized_callback, recv_len = 0, pending_recv = false, @@ -1173,10 +1173,6 @@ emit_stats(State) -> _ -> State1 end. -timestamp({Mega, Sec, Micro}) -> - (Mega * 1000000 * 1000000 + Sec * 1000000 + Micro) div 1000. - - %% 1.0 stub -ifdef(use_specs). -spec(become_1_0/2 :: (non_neg_integer(), #v1{}) -> no_return()). -- cgit v1.2.1 From fa0f2ec486786de7ffaa47d0d4209b364fa51d6e Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 16:30:22 +0400 Subject: Add spec for rabbit_misc:timestamp/1 --- src/rabbit_misc.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 286d8bbc..40afae94 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -255,6 +255,9 @@ -spec(store_proc_name/1 :: (rabbit_types:proc_type_and_name()) -> ok). -spec(moving_average/4 :: (float(), float(), float(), float() | 'undefined') -> float()). +-spec(timestamp/1 :: ({pos_integer(), + pos_integer(), + pos_integer()}) -> pos_integer()). -endif. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 6cae14f9775472c725664bb0f563031be0efa61f Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 25 Jun 2014 16:34:49 +0400 Subject: os:timestamp/1 return parts can be 0 --- src/rabbit_misc.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 40afae94..44ca70ef 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -255,9 +255,9 @@ -spec(store_proc_name/1 :: (rabbit_types:proc_type_and_name()) -> ok). -spec(moving_average/4 :: (float(), float(), float(), float() | 'undefined') -> float()). --spec(timestamp/1 :: ({pos_integer(), - pos_integer(), - pos_integer()}) -> pos_integer()). +-spec(timestamp/1 :: ({non_neg_integer(), + non_neg_integer(), + non_neg_integer()}) -> pos_integer()). -endif. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 576f89c8f89f40e818956996788c871a022123e2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 27 Jun 2014 13:37:16 +0100 Subject: More descriptive name. --- src/rabbit_misc.erl | 6 +++--- src/rabbit_reader.erl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 44ca70ef..91f8b91e 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -71,7 +71,7 @@ -export([get_parent/0]). -export([store_proc_name/1, store_proc_name/2]). -export([moving_average/4]). --export([timestamp/1]). +-export([now_to_ms/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -255,7 +255,7 @@ -spec(store_proc_name/1 :: (rabbit_types:proc_type_and_name()) -> ok). -spec(moving_average/4 :: (float(), float(), float(), float() | 'undefined') -> float()). --spec(timestamp/1 :: ({non_neg_integer(), +-spec(now_to_ms/1 :: ({non_neg_integer(), non_neg_integer(), non_neg_integer()}) -> pos_integer()). -endif. @@ -1020,7 +1020,7 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. -timestamp({Mega, Sec, Micro}) -> +now_to_ms({Mega, Sec, Micro}) -> (Mega * 1000000 * 1000000 + Sec * 1000000 + Micro) div 1000. check_expiry(N) when N < 0 -> {error, {value_negative, N}}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ba62c7a4..70a66bee 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -238,7 +238,7 @@ start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> capabilities = [], auth_mechanism = none, auth_state = none, - connected_at = rabbit_misc:timestamp(os:timestamp())}, + connected_at = rabbit_misc:now_to_ms(os:timestamp())}, callback = uninitialized_callback, recv_len = 0, pending_recv = false, -- cgit v1.2.1 From 1e45541e96b53e17133ae9458b7f0f37c912eccb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 30 Jun 2014 12:09:03 +0100 Subject: Emit an event when we modify a global parameter. --- src/rabbit_runtime_parameters.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index cf125913..f78549ff 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -82,6 +82,8 @@ set(VHost, Component, Name, Term, User) -> set_global(Name, Term) -> mnesia_update(Name, Term), + event_notify(parameter_set, none, global, [{name, Name}, + {value, Term}]), ok. format_error(L) -> @@ -164,6 +166,8 @@ mnesia_clear(VHost, Component, Name) -> event_notify(_Event, _VHost, <<"policy">>, _Props) -> ok; +event_notify(Event, none, Component, Props) -> + rabbit_event:notify(Event, [{component, Component} | Props]); event_notify(Event, VHost, Component, Props) -> rabbit_event:notify(Event, [{vhost, VHost}, {component, Component} | Props]). -- cgit v1.2.1