summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/rabbitmq.conf.example2
-rw-r--r--docs/rabbitmq.config.example4
-rw-r--r--priv/schema/rabbitmq.schema3
-rw-r--r--rabbitmq-components.mk4
-rw-r--r--src/background_gc.erl2
-rw-r--r--src/rabbit.erl79
-rw-r--r--src/rabbit_config.erl1
-rw-r--r--src/rabbit_mnesia.erl46
-rw-r--r--src/rabbit_node_monitor.erl40
-rw-r--r--src/rabbit_peer_discovery.erl55
-rw-r--r--src/rabbit_peer_discovery_classic_config.erl12
-rw-r--r--src/rabbit_peer_discovery_dns.erl11
-rw-r--r--src/rabbit_queue_index.erl2
-rw-r--r--src/term_to_binary_compat.erl32
-rw-r--r--src/vm_memory_monitor.erl21
-rw-r--r--test/backing_queue_SUITE.erl2
-rw-r--r--test/cluster_SUITE.erl2
-rw-r--r--test/cluster_formation_locking_SUITE.erl80
-rw-r--r--test/clustering_management_SUITE.erl8
-rw-r--r--test/partitions_SUITE.erl38
-rw-r--r--test/term_to_binary_compat_prop_SUITE.erl62
-rw-r--r--test/unit_SUITE.erl6
-rw-r--r--test/unit_inbroker_parallel_SUITE.erl2
23 files changed, 431 insertions, 83 deletions
diff --git a/docs/rabbitmq.conf.example b/docs/rabbitmq.conf.example
index 0e8d1e0596..e4c2fff92a 100644
--- a/docs/rabbitmq.conf.example
+++ b/docs/rabbitmq.conf.example
@@ -386,7 +386,7 @@
## Disabling background GC may reduce latency for client operations,
## keeping it enabled may reduce median RAM usage.
##
-# background_gc_enabled = true
+# background_gc_enabled = false
## Target (desired) interval (in milliseconds) at which we run background GC.
## The actual interval will vary depending on how long it takes to execute
diff --git a/docs/rabbitmq.config.example b/docs/rabbitmq.config.example
index 2be3c74095..4135c5053c 100644
--- a/docs/rabbitmq.config.example
+++ b/docs/rabbitmq.config.example
@@ -276,7 +276,7 @@
%% See http://www.rabbitmq.com/partitions.html for further details.
%%
%% {cluster_partition_handling, ignore},
-
+
%% Mirror sync batch size, in messages. Increasing this will speed
%% up syncing but total batch size in bytes must not exceed 2 GiB.
%% Available in RabbitMQ 3.6.0 or later.
@@ -343,7 +343,7 @@
%% Disabling background GC may reduce latency for client operations,
%% keeping it enabled may reduce median RAM usage.
%%
- %% {background_gc_enabled, true},
+ %% {background_gc_enabled, false},
%%
%% Target (desired) interval (in milliseconds) at which we run background GC.
%% The actual interval will vary depending on how long it takes to execute
diff --git a/priv/schema/rabbitmq.schema b/priv/schema/rabbitmq.schema
index ecfa14ded4..985c77b9a8 100644
--- a/priv/schema/rabbitmq.schema
+++ b/priv/schema/rabbitmq.schema
@@ -791,6 +791,9 @@ fun(Conf) ->
dns -> rabbit_peer_discovery_dns;
aws -> rabbit_peer_discovery_aws;
consul -> rabbit_peer_discovery_consul;
+ etcd -> rabbit_peer_discovery_etcd;
+ kubernetes -> rabbit_peer_discovery_k8s;
+ k8s -> rabbit_peer_discovery_k8s;
Module -> Module
end
end}.
diff --git a/rabbitmq-components.mk b/rabbitmq-components.mk
index 1bc8ccf3d5..63bc1c52bd 100644
--- a/rabbitmq-components.mk
+++ b/rabbitmq-components.mk
@@ -76,6 +76,8 @@ dep_rabbitmq_objc_client = git_rmq rabbitmq-objc-client $(current_r
dep_rabbitmq_peer_discovery_aws = git_rmq rabbitmq-peer-discovery-aws $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_peer_discovery_common = git_rmq rabbitmq-peer-discovery-common $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_peer_discovery_consul = git_rmq rabbitmq-peer-discovery-consul $(current_rmq_ref) $(base_rmq_ref) master
+dep_rabbitmq_peer_discovery_etcd = git_rmq rabbitmq-peer-discovery-etcd $(current_rmq_ref) $(base_rmq_ref) master
+dep_rabbitmq_peer_discovery_k8s = git_rmq rabbitmq-peer-discovery-k8s $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_recent_history_exchange = git_rmq rabbitmq-recent-history-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_routing_node_stamp = git_rmq rabbitmq-routing-node-stamp $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_rtopic_exchange = git_rmq rabbitmq-rtopic-exchange $(current_rmq_ref) $(base_rmq_ref) master
@@ -151,6 +153,8 @@ RABBITMQ_COMPONENTS = amqp_client \
rabbitmq_peer_discovery_aws \
rabbitmq_peer_discovery_common \
rabbitmq_peer_discovery_consul \
+ rabbitmq_peer_discovery_etcd \
+ rabbitmq_peer_discovery_k8s \
rabbitmq_recent_history_exchange \
rabbitmq_routing_node_stamp \
rabbitmq_rtopic_exchange \
diff --git a/src/background_gc.erl b/src/background_gc.erl
index 2ae9d93e4e..bbac3138cf 100644
--- a/src/background_gc.erl
+++ b/src/background_gc.erl
@@ -74,7 +74,7 @@ interval_gc(State = #state{last_interval = LastInterval}) ->
State#state{last_interval = Interval}.
gc() ->
- Enabled = rabbit_misc:get_env(rabbit, background_gc_enabled, true),
+ Enabled = rabbit_misc:get_env(rabbit, background_gc_enabled, false),
case Enabled of
true ->
[garbage_collect(P) || P <- processes(),
diff --git a/src/rabbit.erl b/src/rabbit.erl
index 5ae58f57d1..138d03f051 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -19,11 +19,12 @@
-behaviour(application).
-export([start/0, boot/0, stop/0,
- stop_and_halt/0, await_startup/0, status/0, is_running/0, alarms/0,
+ stop_and_halt/0, await_startup/0, await_startup/1,
+ 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_apps/1, stop_apps/1]).
+-export([start_apps/1, start_apps/2, stop_apps/1]).
-export([log_locations/0, config_files/0, decrypt_config/2]). %% for testing and mgmt-agent
-ifdef(TEST).
@@ -266,6 +267,8 @@
-spec boot_delegate() -> 'ok'.
-spec recover() -> 'ok'.
-spec start_apps([app_name()]) -> 'ok'.
+-spec start_apps([app_name()],
+ #{app_name() => permanent|transient|temporary}) -> 'ok'.
-spec stop_apps([app_name()]) -> 'ok'.
%%----------------------------------------------------------------------------
@@ -327,11 +330,7 @@ broker_start() ->
ToBeLoaded = Plugins ++ ?APPS,
start_apps(ToBeLoaded),
maybe_sd_notify(),
- ok = log_broker_started(rabbit_plugins:strictly_plugins(rabbit_plugins:active())),
- %% See rabbitmq/rabbitmq-server#1202 for details.
- rabbit_peer_discovery:maybe_inject_randomized_delay(),
- rabbit_peer_discovery:maybe_register(),
- ok.
+ ok = log_broker_started(rabbit_plugins:strictly_plugins(rabbit_plugins:active())).
%% Try to send systemd ready notification if it makes sense in the
%% current environment. standard_error is used intentionally in all
@@ -471,7 +470,7 @@ stop() ->
undefined -> ok;
_ ->
rabbit_log:info("RabbitMQ hasn't finished starting yet. Waiting for startup to finish before stopping..."),
- wait_for_boot_to_finish()
+ ok = wait_for_boot_to_finish(node())
end,
rabbit_log:info("RabbitMQ is asked to stop...~n", []),
Apps = ?APPS ++ rabbit_plugins:active(),
@@ -506,6 +505,9 @@ stop_and_halt() ->
ok.
start_apps(Apps) ->
+ start_apps(Apps, #{}).
+
+start_apps(Apps, AppModes) ->
app_utils:load_applications(Apps),
ConfigEntryDecoder = case application:get_env(rabbit, config_entry_decoder) of
@@ -545,7 +547,8 @@ start_apps(Apps) ->
true -> ok %% will run during start of rabbit app
end,
ok = app_utils:start_applications(OrderedApps,
- handle_app_error(could_not_start)).
+ handle_app_error(could_not_start),
+ AppModes).
%% This function retrieves the correct IoDevice for requesting
%% input. The problem with using the default IoDevice is that
@@ -621,7 +624,7 @@ decrypt_list([Value|Tail], Algo, Acc) ->
stop_apps(Apps) ->
rabbit_log:info(
- lists:flatten(["Stopping RabbitMQ applications and their dependencies in the following order: ~n",
+ lists:flatten(["Stopping RabbitMQ applications and their dependencies in the following order:~n",
[" ~p~n" || _ <- Apps]]),
lists:reverse(Apps)),
ok = app_utils:stop_applications(
@@ -641,32 +644,52 @@ handle_app_error(Term) ->
end.
await_startup() ->
- case is_booting() of
- true -> wait_for_boot_to_finish();
+ await_startup(node()).
+
+await_startup(Node) ->
+ case is_booting(Node) of
+ true -> wait_for_boot_to_finish(Node);
false ->
- case is_running() of
+ case is_running(Node) of
true -> ok;
- false -> wait_for_boot_to_start(),
- wait_for_boot_to_finish()
+ false -> wait_for_boot_to_start(Node),
+ wait_for_boot_to_finish(Node)
end
end.
-is_booting() ->
- whereis(rabbit_boot) /= undefined.
+is_booting(Node) ->
+ case rpc:call(Node, erlang, whereis, [rabbit_boot]) of
+ {badrpc, _} = Err -> Err;
+ undefined -> false;
+ P when is_pid(P) -> true
+ end.
-wait_for_boot_to_start() ->
- case whereis(rabbit_boot) of
- undefined -> timer:sleep(100),
- wait_for_boot_to_start();
- _ -> ok
+wait_for_boot_to_start(Node) ->
+ case is_booting(Node) of
+ false ->
+ timer:sleep(100),
+ wait_for_boot_to_start(Node);
+ {badrpc, _} = Err ->
+ Err;
+ true ->
+ ok
end.
-wait_for_boot_to_finish() ->
- case whereis(rabbit_boot) of
- undefined -> true = is_running(),
- ok;
- _ -> timer:sleep(100),
- wait_for_boot_to_finish()
+wait_for_boot_to_finish(Node) ->
+ case is_booting(Node) of
+ false ->
+ %% We don't want badrpc error to be interpreted as false,
+ %% so we don't call rabbit:is_running(Node)
+ case rpc:call(Node, rabbit, is_running, []) of
+ true -> ok;
+ false -> {error, rabbit_is_not_running};
+ {badrpc, _} = Err -> Err
+ end;
+ {badrpc, _} = Err ->
+ Err;
+ true ->
+ timer:sleep(100),
+ wait_for_boot_to_finish(Node)
end.
status() ->
diff --git a/src/rabbit_config.erl b/src/rabbit_config.erl
index 9e70898c82..a3f7d19136 100644
--- a/src/rabbit_config.erl
+++ b/src/rabbit_config.erl
@@ -99,7 +99,6 @@ generate_config_file(ConfFiles, ConfDir, ScriptDir) ->
generate_config_file(ConfFiles, ConfDir, ScriptDir, SchemaDir, Advanced) ->
prepare_plugin_schemas(SchemaDir),
- % SchemaFile = filename:join([ScriptDir, "rabbitmq.schema"]),
Cuttlefish = filename:join([ScriptDir, "cuttlefish"]),
GeneratedDir = filename:join([ConfDir, "generated"]),
diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl
index e49ea6dfb6..a0363b69d0 100644
--- a/src/rabbit_mnesia.erl
+++ b/src/rabbit_mnesia.erl
@@ -46,6 +46,11 @@
%% Used internally in rpc calls
-export([node_info/0, remove_node_if_mnesia_running/1]).
+-ifdef(TEST).
+-compile(export_all).
+-export([init_with_lock/3]).
+-endif.
+
-include("rabbit.hrl").
%%----------------------------------------------------------------------------
@@ -101,12 +106,13 @@ init() ->
rabbit_log:info("Node database directory at ~s is empty. "
"Assuming we need to join an existing cluster or initialise from scratch...~n",
[dir()]),
- rabbit_peer_discovery:log_configured_backend(),
- init_from_config();
+ rabbit_peer_discovery:log_configured_backend(),
+ init_with_lock();
false ->
NodeType = node_type(),
init_db_and_upgrade(cluster_nodes(all), NodeType,
- NodeType =:= ram, _Retry = true)
+ NodeType =:= ram, _Retry = true),
+ rabbit_peer_discovery:maybe_register()
end,
%% We intuitively expect the global name server to be synced when
%% Mnesia is up. In fact that's not guaranteed to be the case -
@@ -114,13 +120,43 @@ init() ->
ok = rabbit_node_monitor:global_sync(),
ok.
+init_with_lock() ->
+ {Retries, Timeout} = rabbit_peer_discovery:retry_timeout(),
+ init_with_lock(Retries, Timeout, fun init_from_config/0).
+
+init_with_lock(0, _, InitFromConfig) ->
+ case rabbit_peer_discovery:lock_acquisition_failure_mode() of
+ ignore ->
+ rabbit_log:warning("Cannot acquire a lock during clustering", []),
+ InitFromConfig(),
+ rabbit_peer_discovery:maybe_register();
+ fail ->
+ exit(cannot_acquire_startup_lock)
+ end;
+init_with_lock(Retries, Timeout, InitFromConfig) ->
+ case rabbit_peer_discovery:lock() of
+ not_supported ->
+ %% See rabbitmq/rabbitmq-server#1202 for details.
+ rabbit_peer_discovery:maybe_inject_randomized_delay(),
+ InitFromConfig(),
+ rabbit_peer_discovery:maybe_register();
+ {error, _Reason} ->
+ timer:sleep(Timeout),
+ init_with_lock(Retries - 1, Timeout, InitFromConfig);
+ {ok, Data} ->
+ try
+ InitFromConfig(),
+ rabbit_peer_discovery:maybe_register()
+ after
+ rabbit_peer_discovery:unlock(Data)
+ end
+ end.
+
init_from_config() ->
FindBadNodeNames = fun
(Name, BadNames) when is_atom(Name) -> BadNames;
(Name, BadNames) -> [Name | BadNames]
end,
- %% See rabbitmq/rabbitmq-server#1202 for details.
- rabbit_peer_discovery:maybe_inject_randomized_delay(),
{DiscoveredNodes, NodeType} =
case rabbit_peer_discovery:discover_cluster_nodes() of
{ok, {Nodes, Type} = Config}
diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl
index 0eadf0ff59..810df2d1fc 100644
--- a/src/rabbit_node_monitor.erl
+++ b/src/rabbit_node_monitor.erl
@@ -344,8 +344,8 @@ init([]) ->
Nodes = possibly_partitioned_nodes(),
startup_log(Nodes),
Monitors = lists:foldl(fun(Node, Monitors0) ->
- pmon:monitor({rabbit, Node}, Monitors0)
- end, pmon:new(), Nodes),
+ pmon:monitor({rabbit, Node}, Monitors0)
+ end, pmon:new(), Nodes),
{ok, ensure_keepalive_timer(#state{monitors = Monitors,
subscribers = pmon:new(),
partitions = [],
@@ -420,12 +420,12 @@ handle_cast({check_partial_partition, Node, Rep, NodeGUID, MyGUID, RepGUID},
fun () ->
case rpc:call(Node, rabbit, is_running, []) of
{badrpc, _} -> ok;
- _ ->
- rabbit_log:warning("Received a 'DOWN' message"
- " from ~p but still can"
- " communicate with it ~n",
- [Node]),
- cast(Rep, {partial_partition,
+ _ ->
+ rabbit_log:warning("Received a 'DOWN' message"
+ " from ~p but still can"
+ " communicate with it ~n",
+ [Node]),
+ cast(Rep, {partial_partition,
Node, node(), RepGUID})
end
end);
@@ -499,18 +499,18 @@ handle_cast({node_up, Node, NodeType},
rabbit_log:info("rabbit on node ~p up~n", [Node]),
{AllNodes, DiscNodes, RunningNodes} = read_cluster_status(),
write_cluster_status({add_node(Node, AllNodes),
- case NodeType of
- disc -> add_node(Node, DiscNodes);
- ram -> DiscNodes
- end,
- add_node(Node, RunningNodes)}),
+ case NodeType of
+ disc -> add_node(Node, DiscNodes);
+ ram -> DiscNodes
+ end,
+ add_node(Node, RunningNodes)}),
ok = handle_live_rabbit(Node),
Monitors1 = case pmon:is_monitored({rabbit, Node}, Monitors) of
- true ->
- Monitors;
- false ->
- pmon:monitor({rabbit, Node}, Monitors)
- end,
+ true ->
+ Monitors;
+ false ->
+ pmon:monitor({rabbit, Node}, Monitors)
+ end,
{noreply, maybe_autoheal(State#state{monitors = Monitors1})};
handle_cast({joined_cluster, Node, NodeType}, State) ->
@@ -584,7 +584,7 @@ handle_info({mnesia_system_event,
State1 = case pmon:is_monitored({rabbit, Node}, Monitors) of
true -> State;
false -> State#state{
- monitors = pmon:monitor({rabbit, Node}, Monitors)}
+ monitors = pmon:monitor({rabbit, Node}, Monitors)}
end,
ok = handle_live_rabbit(Node),
Partitions1 = lists:usort([Node | Partitions]),
@@ -893,4 +893,4 @@ startup_log([]) ->
rabbit_log:info("Starting rabbit_node_monitor~n", []);
startup_log(Nodes) ->
rabbit_log:info("Starting rabbit_node_monitor, might be partitioned from ~p~n",
- [Nodes]).
+ [Nodes]).
diff --git a/src/rabbit_peer_discovery.erl b/src/rabbit_peer_discovery.erl
index 628b9f101f..1abf2b7cc7 100644
--- a/src/rabbit_peer_discovery.erl
+++ b/src/rabbit_peer_discovery.erl
@@ -23,8 +23,9 @@
-export([discover_cluster_nodes/0, backend/0, node_type/0,
normalize/1, format_discovered_nodes/1, log_configured_backend/0,
register/0, unregister/0, maybe_register/0, maybe_unregister/0,
- maybe_inject_randomized_delay/0]).
--export([append_node_prefix/1, node_prefix/0]).
+ maybe_inject_randomized_delay/0, lock/0, unlock/1]).
+-export([append_node_prefix/1, node_prefix/0, retry_timeout/0,
+ lock_acquisition_failure_mode/0]).
-define(DEFAULT_BACKEND, rabbit_peer_discovery_classic_config).
%% what node type is used by default for this node when joining
@@ -60,7 +61,27 @@ node_type() ->
?DEFAULT_NODE_TYPE
end.
+-spec retry_timeout() -> {Retries :: integer(), Timeout :: integer()}.
+retry_timeout() ->
+ case application:get_env(rabbit, cluster_formation) of
+ {ok, Proplist} ->
+ Retries = proplists:get_value(lock_retry_limit, Proplist, 10),
+ Timeout = proplists:get_value(lock_retry_timeout, Proplist, 30000),
+ {Retries, Timeout};
+ undefined ->
+ {10, 30000}
+ end.
+
+-spec lock_acquisition_failure_mode() -> ignore | fail.
+
+lock_acquisition_failure_mode() ->
+ case application:get_env(rabbit, cluster_formation) of
+ {ok, Proplist} ->
+ proplists:get_value(lock_acquisition_failure_mode, Proplist, fail);
+ undefined ->
+ fail
+ end.
-spec log_configured_backend() -> ok.
@@ -139,7 +160,7 @@ inject_randomized_delay() ->
true -> Min;
false -> RandomVal
end,
- rabbit_log:info("Will wait for ~p milliseconds before proceeding with regitration...", [Effective]),
+ rabbit_log:info("Will wait for ~p milliseconds before proceeding with registration...", [Effective]),
timer:sleep(Effective),
ok
end.
@@ -183,6 +204,34 @@ unregister() ->
ok
end.
+-spec lock() -> ok | {ok, Data :: term()} | not_supported | {error, Reason :: string()}.
+
+lock() ->
+ Backend = backend(),
+ rabbit_log:info("Will try to lock with peer discovery backend ~s", [Backend]),
+ case Backend:lock(node()) of
+ {error, Reason} = Error ->
+ rabbit_log:error("Failed to lock with peer discovery backend ~s: ~p",
+ [Backend, Reason]),
+ Error;
+ Any ->
+ Any
+ end.
+
+-spec unlock(Data :: term()) -> ok | {error, Reason :: string()}.
+
+unlock(Data) ->
+ Backend = backend(),
+ rabbit_log:info("Will try to unlock with peer discovery backend ~s", [Backend]),
+ case Backend:unlock(Data) of
+ {error, Reason} = Error ->
+ rabbit_log:error("Failed to unlock with peer discovery backend ~s: ~p, "
+ "lock data: ~p",
+ [Backend, Reason, Data]),
+ Error;
+ Any ->
+ Any
+ end.
%%
%% Implementation
diff --git a/src/rabbit_peer_discovery_classic_config.erl b/src/rabbit_peer_discovery_classic_config.erl
index da5fb6c971..96d47d8182 100644
--- a/src/rabbit_peer_discovery_classic_config.erl
+++ b/src/rabbit_peer_discovery_classic_config.erl
@@ -20,7 +20,7 @@
-include("rabbit.hrl").
-export([list_nodes/0, supports_registration/0, register/0, unregister/0,
- post_registration/0]).
+ post_registration/0, lock/1, unlock/1]).
%%
%% API
@@ -54,3 +54,13 @@ unregister() ->
post_registration() ->
ok.
+
+-spec lock(Node :: atom()) -> not_supported.
+
+lock(_Node) ->
+ not_supported.
+
+-spec unlock(Data :: term()) -> ok.
+
+unlock(_Data) ->
+ ok.
diff --git a/src/rabbit_peer_discovery_dns.erl b/src/rabbit_peer_discovery_dns.erl
index 5b1ab4405e..7422876c4d 100644
--- a/src/rabbit_peer_discovery_dns.erl
+++ b/src/rabbit_peer_discovery_dns.erl
@@ -20,7 +20,7 @@
-include("rabbit.hrl").
-export([list_nodes/0, supports_registration/0, register/0, unregister/0,
- post_registration/0]).
+ post_registration/0, lock/1, unlock/1]).
%% for tests
-export([discover_nodes/2, discover_hostnames/2]).
@@ -71,6 +71,15 @@ unregister() ->
post_registration() ->
ok.
+-spec lock(Node :: atom()) -> not_supported.
+
+lock(_Node) ->
+ not_supported.
+
+-spec unlock(Data :: term()) -> ok.
+
+unlock(_Data) ->
+ ok.
%%
%% Implementation
diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl
index a71eaf1ff4..123bfaba25 100644
--- a/src/rabbit_queue_index.erl
+++ b/src/rabbit_queue_index.erl
@@ -691,7 +691,7 @@ recover_message(false, _, no_del, RelSeq, {Segment, DirtyCount}) ->
DirtyCount + 2}.
queue_name_to_dir_name(Name = #resource { kind = queue }) ->
- <<Num:128>> = erlang:md5(term_to_binary(Name)),
+ <<Num:128>> = erlang:md5(term_to_binary_compat:queue_name_to_binary(Name)),
rabbit_misc:format("~.36B", [Num]).
queues_base_dir() ->
diff --git a/src/term_to_binary_compat.erl b/src/term_to_binary_compat.erl
new file mode 100644
index 0000000000..8a74bde3e0
--- /dev/null
+++ b/src/term_to_binary_compat.erl
@@ -0,0 +1,32 @@
+%% 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) 2017 Pivotal Software, Inc. All rights reserved.
+%%
+
+-module(term_to_binary_compat).
+
+-include("rabbit.hrl").
+
+-export([queue_name_to_binary/1]).
+
+queue_name_to_binary(#resource{kind = queue, virtual_host = VHost, name = Name}) ->
+ VHostBSize = byte_size(VHost),
+ NameBSize = byte_size(Name),
+ <<131, %% Binary format "version"
+ 104, 4, %% 4-element tuple
+ 100, 0, 8, "resource", %% `resource` atom
+ 109, VHostBSize:32, VHost/binary, %% Vhost binary
+ 100, 0, 5, "queue", %% `queue` atom
+ 109, NameBSize:32, Name/binary>>. %% Name binary
+
diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl
index ba324d649e..71e9f36c46 100644
--- a/src/vm_memory_monitor.erl
+++ b/src/vm_memory_monitor.erl
@@ -43,7 +43,7 @@
-define(SERVER, ?MODULE).
-define(DEFAULT_MEMORY_CHECK_INTERVAL, 1000).
--define(ONE_MB, 1048576).
+-define(ONE_MiB, 1048576).
%% For an unknown OS, we assume that we have 1GB of memory. It'll be
%% wrong. Scale by vm_memory_high_watermark in configuration to get a
@@ -214,9 +214,10 @@ set_mem_limits(State, MemLimit) ->
memory_limit = undefined } ->
error_logger:warning_msg(
"Unknown total memory size for your OS ~p. "
- "Assuming memory size is ~pMB.~n",
+ "Assuming memory size is ~p MiB (~p bytes).~n",
[os:type(),
- trunc(?MEMORY_SIZE_FOR_UNKNOWN_OS/?ONE_MB)]);
+ trunc(?MEMORY_SIZE_FOR_UNKNOWN_OS/?ONE_MiB),
+ ?MEMORY_SIZE_FOR_UNKNOWN_OS]);
_ ->
ok
end,
@@ -227,18 +228,20 @@ set_mem_limits(State, MemLimit) ->
case get_vm_limit() of
Limit when Limit < TotalMemory ->
error_logger:warning_msg(
- "Only ~pMB of ~pMB memory usable due to "
+ "Only ~p MiB (~p bytes) of ~p MiB (~p bytes) memory usable due to "
"limited address space.~n"
"Crashes due to memory exhaustion are possible - see~n"
"http://www.rabbitmq.com/memory.html#address-space~n",
- [trunc(V/?ONE_MB) || V <- [Limit, TotalMemory]]),
+ [trunc(Limit/?ONE_MiB), Limit, trunc(TotalMemory/?ONE_MiB),
+ TotalMemory]),
Limit;
_ ->
TotalMemory
end,
MemLim = interpret_limit(parse_mem_limit(MemLimit), UsableMemory),
- error_logger:info_msg("Memory limit set to ~pMB of ~pMB total.~n",
- [trunc(MemLim/?ONE_MB), trunc(TotalMemory/?ONE_MB)]),
+ error_logger:info_msg("Memory limit set to ~p MiB (~p bytes) of ~p MiB (~p bytes) total.~n",
+ [trunc(MemLim/?ONE_MiB), MemLim, trunc(TotalMemory/?ONE_MiB),
+ TotalMemory]),
internal_update(State #state { total_memory = TotalMemory,
memory_limit = MemLim,
memory_config_limit = MemLimit}).
@@ -402,9 +405,9 @@ parse_line_sunos(Line) ->
[Value1 | UnitsRest] = string:tokens(RHS, " "),
Value2 = case UnitsRest of
["Gigabytes"] ->
- list_to_integer(Value1) * ?ONE_MB * 1024;
+ list_to_integer(Value1) * ?ONE_MiB * 1024;
["Megabytes"] ->
- list_to_integer(Value1) * ?ONE_MB;
+ list_to_integer(Value1) * ?ONE_MiB;
["Kilobytes"] ->
list_to_integer(Value1) * 1024;
_ ->
diff --git a/test/backing_queue_SUITE.erl b/test/backing_queue_SUITE.erl
index 3ff215f497..60f86e0542 100644
--- a/test/backing_queue_SUITE.erl
+++ b/test/backing_queue_SUITE.erl
@@ -1256,7 +1256,7 @@ make_publish_delivered(IsPersistent, PayloadFun, PropFun, N) ->
PropFun(N, #message_properties{size = 10})}.
queue_name(Config, Name) ->
- Name1 = rabbit_ct_helpers:config_to_testcase_name(Config, Name),
+ Name1 = iolist_to_binary(rabbit_ct_helpers:config_to_testcase_name(Config, Name)),
queue_name(Name1).
queue_name(Name) ->
diff --git a/test/cluster_SUITE.erl b/test/cluster_SUITE.erl
index bc442ecba7..3dba65ae1f 100644
--- a/test/cluster_SUITE.erl
+++ b/test/cluster_SUITE.erl
@@ -355,7 +355,7 @@ test_spawn_remote() ->
end.
queue_name(Config, Name) ->
- Name1 = rabbit_ct_helpers:config_to_testcase_name(Config, Name),
+ Name1 = iolist_to_binary(rabbit_ct_helpers:config_to_testcase_name(Config, Name)),
queue_name(Name1).
queue_name(Name) ->
diff --git a/test/cluster_formation_locking_SUITE.erl b/test/cluster_formation_locking_SUITE.erl
new file mode 100644
index 0000000000..25b2df308c
--- /dev/null
+++ b/test/cluster_formation_locking_SUITE.erl
@@ -0,0 +1,80 @@
+%% 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-2017 Pivotal Software, Inc. All rights reserved.
+%%
+-module(cluster_formation_locking_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, non_parallel_tests}
+ ].
+
+groups() ->
+ [
+ {non_parallel_tests, [], [
+ init_with_lock_exits_after_errors,
+ init_with_lock_ignore_after_errors,
+ init_with_lock_not_supported,
+ init_with_lock_supported
+ ]}
+ ].
+
+init_per_testcase(Testcase, Config) when Testcase == init_with_lock_exits_after_errors;
+ Testcase == init_with_lock_not_supported;
+ Testcase == init_with_lock_supported ->
+ application:set_env(rabbit, cluster_formation,
+ [{peer_discover_backend, peer_discover_classic_config},
+ {lock_acquisition_failure_mode, fail}]),
+ ok = meck:new(rabbit_peer_discovery_classic_config, [passthrough]),
+ Config;
+init_per_testcase(init_with_lock_ignore_after_errors, Config) ->
+ application:set_env(rabbit, cluster_formation,
+ [{peer_discover_backend, peer_discover_classic_config},
+ {lock_acquisition_failure_mode, ignore}]),
+ ok = meck:new(rabbit_peer_discovery_classic_config, [passthrough]),
+ Config.
+
+end_per_testcase(_, _) ->
+ meck:unload(),
+ application:unset_env(rabbit, cluster_formation).
+
+init_with_lock_exits_after_errors(_Config) ->
+ meck:expect(rabbit_peer_discovery_classic_config, lock, fun(_) -> {error, "test error"} end),
+ ?assertExit(cannot_acquire_startup_lock, rabbit_mnesia:init_with_lock(2, 10, fun() -> ok end)),
+ ?assert(meck:validate(rabbit_peer_discovery_classic_config)),
+ passed.
+
+init_with_lock_ignore_after_errors(_Config) ->
+ meck:expect(rabbit_peer_discovery_classic_config, lock, fun(_) -> {error, "test error"} end),
+ ?assertEqual(ok, rabbit_mnesia:init_with_lock(2, 10, fun() -> ok end)),
+ ?assert(meck:validate(rabbit_peer_discovery_classic_config)),
+ passed.
+
+init_with_lock_not_supported(_Config) ->
+ meck:expect(rabbit_peer_discovery_classic_config, lock, fun(_) -> not_supported end),
+ ?assertEqual(ok, rabbit_mnesia:init_with_lock(2, 10, fun() -> ok end)),
+ ?assert(meck:validate(rabbit_peer_discovery_classic_config)),
+ passed.
+
+init_with_lock_supported(_Config) ->
+ meck:expect(rabbit_peer_discovery_classic_config, lock, fun(_) -> {ok, data} end),
+ meck:expect(rabbit_peer_discovery_classic_config, unlock, fun(data) -> ok end),
+ ?assertEqual(ok, rabbit_mnesia:init_with_lock(2, 10, fun() -> ok end)),
+ ?assert(meck:validate(rabbit_peer_discovery_classic_config)),
+ passed.
diff --git a/test/clustering_management_SUITE.erl b/test/clustering_management_SUITE.erl
index b0a93ad208..51c0928ba5 100644
--- a/test/clustering_management_SUITE.erl
+++ b/test/clustering_management_SUITE.erl
@@ -135,7 +135,8 @@ join_and_part_cluster(Config) ->
%% Allow clustering with already clustered node
ok = stop_app(Rabbit),
- {ok, already_member} = join_cluster(Rabbit, Hare),
+ {ok, <<"The node is already a member of this cluster">>} =
+ join_cluster(Rabbit, Hare),
ok = start_app(Rabbit),
stop_reset_start(Rabbit),
@@ -388,10 +389,9 @@ force_boot(Config) ->
change_cluster_node_type(Config) ->
[Rabbit, Hare, _Bunny] = cluster_members(Config),
- %% Trying to change the ram node when not clustered should always fail
+ %% Trying to change the node to the ram type when not clustered should always fail
ok = stop_app(Rabbit),
assert_failure(fun () -> change_cluster_node_type(Rabbit, ram) end),
- assert_failure(fun () -> change_cluster_node_type(Rabbit, disc) end),
ok = start_app(Rabbit),
ok = stop_app(Rabbit),
@@ -643,7 +643,7 @@ wait_for_pid_file_to_contain_running_process_pid(PidFile, Attempts, Timeout) ->
Pid = pid_from_file(PidFile),
case rabbit_misc:is_os_process_alive(Pid) of
true -> ok;
- false ->
+ false ->
ct:sleep(Timeout),
wait_for_pid_file_to_contain_running_process_pid(PidFile, Attempts - 1, Timeout)
end.
diff --git a/test/partitions_SUITE.erl b/test/partitions_SUITE.erl
index 8c8a772987..b09d05b550 100644
--- a/test/partitions_SUITE.erl
+++ b/test/partitions_SUITE.erl
@@ -335,16 +335,26 @@ autoheal_unexpected_finish(Config) ->
partial_false_positive(Config) ->
[A, B, C] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
+ suspend_node_monitor(Config, C),
block([{A, B}]),
timer:sleep(1000),
block([{A, C}]),
timer:sleep(?DELAY),
+ resume_node_monitor(Config, C),
+ timer:sleep(?DELAY),
unblock([{A, B}, {A, C}]),
timer:sleep(?DELAY),
%% When B times out A's connection, it will check with C. C will
%% not have timed out A yet, but already it can't talk to it. We
%% need to not consider this a partial partition; B and C should
%% still talk to each other.
+ %%
+ %% Because there is a chance that C can still talk to A when B
+ %% requests to check for a partial partition, we suspend C's
+ %% rabbit_node_monitor at the beginning and resume it after the
+ %% link between A and C is blocked. This way, when B asks C about
+ %% A, we make sure that the A<->C link is blocked before C's
+ %% rabbit_node_monitor processes B's request.
[B, C] = partitions(A),
[A] = partitions(B),
[A] = partitions(C),
@@ -369,7 +379,19 @@ partial_to_full(Config) ->
partial_pause_minority(Config) ->
[A, B, C] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
set_mode(Config, pause_minority),
+ %% We suspend rabbit_node_monitor on C while we block the link
+ %% between A and B. This should make sure C's rabbit_node_monitor
+ %% processes both partial partition checks from A and B at about
+ %% the same time, and thus increase the chance both A and B decides
+ %% there is a partial partition.
+ %%
+ %% Without this, one node may see the partial partition and stop,
+ %% before the other node sees it. In this case, the other node
+ %% doesn't stop and this testcase fails.
+ suspend_node_monitor(Config, C),
block([{A, B}]),
+ timer:sleep(?DELAY),
+ resume_node_monitor(Config, C),
[await_running(N, false) || N <- [A, B]],
await_running(C, true),
unblock([{A, B}]),
@@ -394,6 +416,22 @@ set_mode(Config, Mode) ->
set_mode(Config, Nodes, Mode) ->
rabbit_ct_broker_helpers:set_partition_handling_mode(Config, Nodes, Mode).
+suspend_node_monitor(Config, Node) ->
+ rabbit_ct_broker_helpers:rpc(
+ Config, Node, ?MODULE, suspend_or_resume_node_monitor, [suspend]).
+
+resume_node_monitor(Config, Node) ->
+ rabbit_ct_broker_helpers:rpc(
+ Config, Node, ?MODULE, suspend_or_resume_node_monitor, [resume]).
+
+suspend_or_resume_node_monitor(SuspendOrResume) ->
+ Action = case SuspendOrResume of
+ suspend -> "Suspending";
+ resume -> "Resuming"
+ end,
+ rabbit_log:info("(~s) ~s node monitor~n", [?MODULE, Action]),
+ ok = sys:SuspendOrResume(rabbit_node_monitor).
+
block_unblock(Pairs) ->
block(Pairs),
timer:sleep(?DELAY),
diff --git a/test/term_to_binary_compat_prop_SUITE.erl b/test/term_to_binary_compat_prop_SUITE.erl
new file mode 100644
index 0000000000..d09b23c9ea
--- /dev/null
+++ b/test/term_to_binary_compat_prop_SUITE.erl
@@ -0,0 +1,62 @@
+%% 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) 2017 Pivotal Software, Inc. All rights reserved.
+%%
+
+
+-module(term_to_binary_compat_prop_SUITE).
+
+-compile(export_all).
+
+-include("rabbit.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include_lib("proper/include/proper.hrl").
+
+all() ->
+ %% The test should run on OTP < 20 (erts < 9)
+ case erts_gt_8() of
+ true ->
+ [];
+ false ->
+ [queue_name_to_binary]
+ end.
+
+erts_gt_8() ->
+ Vsn = erlang:system_info(version),
+ [Maj|_] = string:tokens(Vsn, "."),
+ list_to_integer(Maj) > 8.
+
+init_per_suite(Config) ->
+ rabbit_ct_helpers:log_environment(),
+ rabbit_ct_helpers:run_setup_steps(Config).
+
+end_per_suite(Config) ->
+ rabbit_ct_helpers:run_teardown_steps(Config).
+
+init_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_started(Config, Testcase).
+
+queue_name_to_binary(Config) ->
+ Fun = fun () -> prop_queue_name_to_binary(Config) end,
+ rabbit_ct_proper_helpers:run_proper(Fun, [], 10000).
+
+
+prop_queue_name_to_binary(_Config) ->
+ ?FORALL({Vhost, QName}, {binary(), binary()},
+ begin
+ Resource = rabbit_misc:r(Vhost, queue, QName),
+ Legacy = term_to_binary_compat:queue_name_to_binary(Resource),
+ Current = term_to_binary(Resource),
+ Current =:= Legacy
+ end). \ No newline at end of file
diff --git a/test/unit_SUITE.erl b/test/unit_SUITE.erl
index f3fec06cb4..3e92158595 100644
--- a/test/unit_SUITE.erl
+++ b/test/unit_SUITE.erl
@@ -332,7 +332,7 @@ do_decrypt_start_app(Config, Passphrase) ->
%%
%% We expect a failure *after* the decrypting has been done.
try
- rabbit:start_apps([rabbit_shovel_test])
+ rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
catch _:_ ->
ok
end,
@@ -359,7 +359,7 @@ decrypt_start_app_undefined(Config) ->
%%
%% We expect a failure during decryption because the passphrase is missing.
try
- rabbit:start_apps([rabbit_shovel_test])
+ rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
catch
exit:{bad_configuration, config_entry_decoder} -> ok;
_:_ -> exit(unexpected_exception)
@@ -379,7 +379,7 @@ decrypt_start_app_wrong_passphrase(Config) ->
%%
%% We expect a failure during decryption because the passphrase is wrong.
try
- rabbit:start_apps([rabbit_shovel_test])
+ rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
catch
exit:{decryption_error,_,_} -> ok;
_:_ -> exit(unexpected_exception)
diff --git a/test/unit_inbroker_parallel_SUITE.erl b/test/unit_inbroker_parallel_SUITE.erl
index f9cfd58eaa..9c274ab525 100644
--- a/test/unit_inbroker_parallel_SUITE.erl
+++ b/test/unit_inbroker_parallel_SUITE.erl
@@ -146,7 +146,7 @@ on_disk_stop(Pid) ->
end.
queue_name(Config, Name) ->
- Name1 = rabbit_ct_helpers:config_to_testcase_name(Config, Name),
+ Name1 = iolist_to_binary(rabbit_ct_helpers:config_to_testcase_name(Config, Name)),
queue_name(Name1).
queue_name(Name) ->