diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/background_gc.erl | 21 | ||||
| -rw-r--r-- | src/rabbit.app.src | 4 | ||||
| -rw-r--r-- | src/rabbit.erl | 8 | ||||
| -rw-r--r-- | src/rabbit_plugins.erl | 146 | ||||
| -rw-r--r-- | src/rabbit_plugins_main.erl | 39 |
5 files changed, 157 insertions, 61 deletions
diff --git a/src/background_gc.erl b/src/background_gc.erl index 2986f356f5..835eaef4df 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -25,7 +25,6 @@ terminate/2, code_change/3]). -define(MAX_RATIO, 0.01). --define(IDEAL_INTERVAL, 60000). -define(MAX_INTERVAL, 240000). -record(state, {last_interval}). @@ -45,7 +44,9 @@ run() -> gen_server2:cast(?MODULE, run). %%---------------------------------------------------------------------------- -init([]) -> {ok, interval_gc(#state{last_interval = ?IDEAL_INTERVAL})}. +init([]) -> + {ok, IdealInterval} = application:get_env(rabbit, background_gc_target_interval), + {ok, interval_gc(#state{last_interval = IdealInterval})}. handle_call(Msg, _From, State) -> {stop, {unexpected_call, Msg}, {unexpected_call, Msg}, State}. @@ -65,14 +66,22 @@ terminate(_Reason, State) -> State. %%---------------------------------------------------------------------------- interval_gc(State = #state{last_interval = LastInterval}) -> + {ok, IdealInterval} = application:get_env(rabbit, background_gc_target_interval), {ok, Interval} = rabbit_misc:interval_operation( {?MODULE, gc, []}, - ?MAX_RATIO, ?MAX_INTERVAL, ?IDEAL_INTERVAL, LastInterval), + ?MAX_RATIO, ?MAX_INTERVAL, IdealInterval, LastInterval), erlang:send_after(Interval, self(), run), State#state{last_interval = Interval}. gc() -> - [garbage_collect(P) || P <- processes(), - {status, waiting} == process_info(P, status)], - garbage_collect(), %% since we will never be waiting... + Enabled = rabbit_misc:get_env(rabbit, background_gc_enabled, true), + case Enabled of + true -> + [garbage_collect(P) || P <- processes(), + {status, waiting} == process_info(P, status)], + %% since we will never be waiting... + garbage_collect(); + false -> + ok + end, ok. diff --git a/src/rabbit.app.src b/src/rabbit.app.src index 250b14bc32..5f3120b117 100644 --- a/src/rabbit.app.src +++ b/src/rabbit.app.src @@ -117,5 +117,7 @@ ]}, %% rabbitmq-server-973 - {lazy_queue_explicit_gc_run_operation_threshold, 250} + {lazy_queue_explicit_gc_run_operation_threshold, 250}, + {background_gc_enabled, true}, + {background_gc_target_interval, 60000} ]}]}. diff --git a/src/rabbit.erl b/src/rabbit.erl index 6dde2aca88..ebc92150eb 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, alarms/0, is_running/1, environment/0, rotate_logs/0, force_event_refresh/1, start_fhc/0]). --export([start/2, stop/1, prep_stop/1]). +-export([start/2, stop/1]). -export([start_apps/1, stop_apps/1]). -export([log_locations/0, config_files/0, decrypt_config/2]). %% for testing and mgmt-agent @@ -727,7 +727,7 @@ start(normal, []) -> Error end. -prep_stop(_State) -> +stop(_State) -> ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of true -> ok; @@ -735,9 +735,7 @@ prep_stop(_State) -> end, ok. -stop(_) -> ok. - --spec boot_error(atom(), term()) -> no_return(). +-spec boot_error(term(), not_available | [tuple()]) -> no_return(). boot_error(_, {could_not_start, rabbit, {{timeout_waiting_for_tables, _}, _}}) -> AllNodes = rabbit_mnesia:cluster_nodes(all), diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 723f725303..3f65452bdf 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -134,46 +134,16 @@ active() -> lists:member(App, InstalledPlugins)]. %% @doc Get the list of plugins which are ready to be enabled. -list(PluginsDir) -> - list(PluginsDir, false). +list(PluginsPath) -> + list(PluginsPath, false). -list(PluginsDir, IncludeRequiredDeps) -> - EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], - FreeApps = [{app, App} || - App <- filelib:wildcard("*/ebin/*.app", PluginsDir)], - %% We load the "rabbit" application to be sure we can get the - %% "applications" key. This is required for rabbitmq-plugins for - %% instance. - application:load(rabbit), - {ok, RabbitDeps} = application:get_key(rabbit, applications), - AllPlugins = [plugin_info(PluginsDir, Plug) || Plug <- EZs ++ FreeApps], - {AvailablePlugins, Problems} = - lists:foldl( - fun ({error, EZ, Reason}, {Plugins1, Problems1}) -> - {Plugins1, [{EZ, Reason} | Problems1]}; - (Plugin = #plugin{name = Name}, - {Plugins1, Problems1}) -> - %% Applications RabbitMQ depends on (eg. - %% "rabbit_common") can't be considered - %% plugins, otherwise rabbitmq-plugins would - %% list them and the user may believe he can - %% disable them. - case IncludeRequiredDeps orelse - not lists:member(Name, RabbitDeps) of - true -> {[Plugin|Plugins1], Problems1}; - false -> {Plugins1, Problems1} - end - end, {[], []}, - AllPlugins), - case Problems of - [] -> ok; - _ -> rabbit_log:warning( - "Problem reading some plugins: ~p~n", [Problems]) - end, - - Plugins = lists:filter(fun(P) -> not plugin_provided_by_otp(P) end, - AvailablePlugins), - ensure_dependencies(Plugins). +list(PluginsPath, IncludeRequiredDeps) -> + {AllPlugins, LoadingProblems} = discover_plugins(split_path(PluginsPath)), + {UniquePlugins, DuplicateProblems} = remove_duplicate_plugins(AllPlugins), + Plugins1 = maybe_keep_required_deps(IncludeRequiredDeps, UniquePlugins), + Plugins2 = remove_otp_overrideable_plugins(Plugins1), + maybe_report_plugin_loading_problems(LoadingProblems ++ DuplicateProblems), + ensure_dependencies(Plugins2). %% @doc Read the list of enabled plugins from the supplied term file. read_enabled(PluginsFile) -> @@ -425,14 +395,12 @@ prepare_plugin(#plugin{type = dir, name = Name, location = Location}, ExpandDir) -> rabbit_file:recursive_copy(Location, filename:join([ExpandDir, Name])). -plugin_info(Base, {ez, EZ0}) -> - EZ = filename:join([Base, EZ0]), +plugin_info({ez, EZ}) -> case read_app_file(EZ) of {application, Name, Props} -> mkplugin(Name, Props, ez, EZ); {error, Reason} -> {error, EZ, Reason} end; -plugin_info(Base, {app, App0}) -> - App = filename:join([Base, App0]), +plugin_info({app, App}) -> case rabbit_file:read_term_file(App) of {ok, [{application, Name, Props}]} -> mkplugin(Name, Props, dir, @@ -486,9 +454,99 @@ plugin_names(Plugins) -> [Name || #plugin{name = Name} <- Plugins]. lookup_plugins(Names, AllPlugins) -> - % Preserve order of Names + %% Preserve order of Names lists:map( fun(Name) -> lists:keyfind(Name, #plugin.name, AllPlugins) end, Names). + +%% Split PATH-like value into its components. +split_path(PathString) -> + Delimiters = case os:type() of + {unix, _} -> ":"; + {win32, _} -> ";" + end, + string:tokens(PathString, Delimiters). + +%% Search for files using glob in a given dir. Returns full filenames of those files. +full_path_wildcard(Glob, Dir) -> + [filename:join([Dir, File]) || File <- filelib:wildcard(Glob, Dir)]. + +%% Returns list off all .ez files in a given set of directories +list_ezs([]) -> + []; +list_ezs([Dir|Rest]) -> + [{ez, EZ} || EZ <- full_path_wildcard("*.ez", Dir)] ++ list_ezs(Rest). + +%% Returns list of all files that look like OTP applications in a +%% given set of directories. +list_free_apps([]) -> + []; +list_free_apps([Dir|Rest]) -> + [{app, App} || App <- full_path_wildcard("*/ebin/*.app", Dir)] + ++ list_free_apps(Rest). + +compare_by_name_and_version(#plugin{name = Name, version = VersionA}, + #plugin{name = Name, version = VersionB}) -> + ec_semver:lte(VersionA, VersionB); +compare_by_name_and_version(#plugin{name = NameA}, + #plugin{name = NameB}) -> + NameA =< NameB. + +-spec discover_plugins([Directory]) -> {[#plugin{}], [Problem]} when + Directory :: file:name(), + Problem :: {file:name(), term()}. +discover_plugins(PluginsDirs) -> + EZs = list_ezs(PluginsDirs), + FreeApps = list_free_apps(PluginsDirs), + read_plugins_info(EZs ++ FreeApps, {[], []}). + +read_plugins_info([], Acc) -> + Acc; +read_plugins_info([Path|Paths], {Plugins, Problems}) -> + case plugin_info(Path) of + #plugin{} = Plugin -> + read_plugins_info(Paths, {[Plugin|Plugins], Problems}); + {error, Location, Reason} -> + read_plugins_info(Paths, {Plugins, [{Location, Reason}|Problems]}) + end. + +remove_duplicate_plugins(Plugins) -> + %% Reverse order ensures that if there are several versions of the + %% same plugin, the most recent one comes first. + Sorted = lists:reverse( + lists:sort(fun compare_by_name_and_version/2, Plugins)), + remove_duplicate_plugins(Sorted, {[], []}). + +remove_duplicate_plugins([], Acc) -> + Acc; +remove_duplicate_plugins([Best = #plugin{name = Name}, Offender = #plugin{name = Name} | Rest], + {Plugins0, Problems0}) -> + Problems1 = [{Offender#plugin.location, duplicate_plugin}|Problems0], + remove_duplicate_plugins([Best|Rest], {Plugins0, Problems1}); +remove_duplicate_plugins([Plugin|Rest], {Plugins0, Problems0}) -> + Plugins1 = [Plugin|Plugins0], + remove_duplicate_plugins(Rest, {Plugins1, Problems0}). + +maybe_keep_required_deps(true, Plugins) -> + Plugins; +maybe_keep_required_deps(false, Plugins) -> + %% We load the "rabbit" application to be sure we can get the + %% "applications" key. This is required for rabbitmq-plugins for + %% instance. + application:load(rabbit), + {ok, RabbitDeps} = application:get_key(rabbit, applications), + lists:filter(fun(#plugin{name = Name}) -> + not lists:member(Name, RabbitDeps) + end, + Plugins). + +remove_otp_overrideable_plugins(Plugins) -> + lists:filter(fun(P) -> not plugin_provided_by_otp(P) end, + Plugins). + +maybe_report_plugin_loading_problems([]) -> + ok; +maybe_report_plugin_loading_problems(Problems) -> + rabbit_log:warning("Problem reading some plugins: ~p~n", [Problems]). diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 4618249ee8..dccdd92969 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -191,9 +191,9 @@ format_plugins(Node, Pattern, Opts, #cli{all = All, EnabledImplicitly = Implicit -- Enabled, {StatusMsg, Running} = - case rabbit_misc:rpc_call(Node, rabbit_plugins, active, []) of - {badrpc, _} -> {"[failed to contact ~s - status not shown]", []}; - Active -> {"* = running on ~s", Active} + case remote_running_plugins(Node) of + {ok, Active} -> {"* = running on ~s", Active}; + error -> {"[failed to contact ~s - status not shown]", []} end, {ok, RE} = re:compile(Pattern), Plugins = [ Plugin || @@ -218,7 +218,7 @@ format_plugins(Node, Pattern, Opts, #cli{all = All, Format, MaxWidth) || P <- Plugins1], ok. -format_plugin(#plugin{name = Name, version = Version, +format_plugin(#plugin{name = Name, version = OnDiskVersion, description = Description, dependencies = Deps}, Enabled, EnabledImplicitly, Running, Format, MaxWidth) -> @@ -228,7 +228,7 @@ format_plugin(#plugin{name = Name, version = Version, {false, true} -> "e"; _ -> " " end, - RunningGlyph = case lists:member(Name, Running) of + RunningGlyph = case lists:keymember(Name, 1, Running) of true -> "*"; false -> " " end, @@ -236,6 +236,7 @@ format_plugin(#plugin{name = Name, version = Version, Opt = fun (_F, A, A) -> ok; ( F, A, _) -> io:format(F, [A]) end, + Version = format_running_plugin_version(Name, OnDiskVersion, Running), case Format of minimal -> io:format("~s~n", [Name]); normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w ", @@ -327,3 +328,31 @@ rpc_call(Node, Online, Mod, Fun, Args) -> plur([_]) -> ""; plur(_) -> "s". + +-spec remote_running_plugins(node()) -> [{atom(), Vsn :: string()}]. +remote_running_plugins(Node) -> + case rabbit_misc:rpc_call(Node, rabbit_plugins, active, []) of + {badrpc, _} -> error; + Active -> maybe_augment_with_versions(Node, Active) + end. + +-spec maybe_augment_with_versions(node(), [atom()]) -> [{atom(), Vsn :: string()}]. +maybe_augment_with_versions(Node, Plugins) -> + case rabbit_misc:rpc_call(Node, rabbit_misc, which_applications, []) of + {badrpc, _} -> + error; + All -> + {ok, [{App, Vsn} || {App, _, Vsn} <- All, + lists:member(App, Plugins)]} + end. + +-spec format_running_plugin_version(atom(), string(), [{atom(), Vsn :: string()}]) -> string(). +format_running_plugin_version(Name, OnDiskVersion, RunningPlugins) -> + case lists:keyfind(Name, 1, RunningPlugins) of + false -> + OnDiskVersion; + {_, OnDiskVersion} -> + OnDiskVersion; + {_, RunningVersion} -> + io_lib:format("~s (pending upgrade to ~s)", [RunningVersion, OnDiskVersion]) + end. |
