summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon MacMullen <simon@rabbitmq.com>2014-05-09 15:27:34 +0100
committerSimon MacMullen <simon@rabbitmq.com>2014-05-09 15:27:34 +0100
commit6b927367091d058247aa4d0f4c2ec93a5cb8999d (patch)
tree1beae751c41b0f32ea056937302930fb31fe895e
parenta272278f6762be5a96a494b4d228d0b184466df0 (diff)
parente787dcfdae7bc055e2ee13c70ed14b3eacba5a9e (diff)
downloadrabbitmq-server-git-6b927367091d058247aa4d0f4c2ec93a5cb8999d.tar.gz
Merge bug26120
-rw-r--r--Makefile4
-rw-r--r--include/rabbit.hrl2
-rw-r--r--packaging/RPMS/Fedora/rabbitmq-server.spec3
-rw-r--r--packaging/debs/Debian/debian/changelog6
-rw-r--r--src/gm.erl249
-rw-r--r--src/rabbit_mirror_queue_misc.erl24
-rw-r--r--src/rabbit_mirror_queue_slave.erl8
-rw-r--r--src/rabbit_nodes.erl33
-rw-r--r--src/rabbit_tests.erl10
-rw-r--r--src/truncate.erl30
10 files changed, 182 insertions, 187 deletions
diff --git a/Makefile b/Makefile
index 81a477ae6a..127fa2fe5a 100644
--- a/Makefile
+++ b/Makefile
@@ -250,9 +250,6 @@ start-cover: all
echo "rabbit_misc:start_cover([\"rabbit\", \"hare\"])." | $(ERL_CALL)
echo "rabbit_misc:enable_cover([\"$(COVER_DIR)\"])." | $(ERL_CALL)
-start-secondary-cover: all
- echo "rabbit_misc:start_cover([\"hare\"])." | $(ERL_CALL)
-
stop-cover: all
echo "rabbit_misc:report_cover(), cover:stop()." | $(ERL_CALL)
cat cover/summary.txt
@@ -384,3 +381,4 @@ include $(DEPS_FILE)
endif
.PHONY: run-qc
+
diff --git a/include/rabbit.hrl b/include/rabbit.hrl
index 12b7a07b35..44f0931eef 100644
--- a/include/rabbit.hrl
+++ b/include/rabbit.hrl
@@ -125,6 +125,6 @@
%% 4) Amount to decrease 2) every time we descend while truncating.
%%
%% Whole thing feeds into truncate:log_event/2.
--define(LOG_TRUNC, {2000, 100, 100, 7}).
+-define(LOG_TRUNC, {2000, 100, 50, 5}).
-define(store_proc_name(N), rabbit_misc:store_proc_name(?MODULE, N)).
diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec
index b85f9a0269..618e3a5662 100644
--- a/packaging/RPMS/Fedora/rabbitmq-server.spec
+++ b/packaging/RPMS/Fedora/rabbitmq-server.spec
@@ -130,6 +130,9 @@ done
rm -rf %{buildroot}
%changelog
+* Tue Apr 29 2014 simon@rabbitmq.com 3.3.1-1
+- New Upstream Release
+
* Wed Apr 2 2014 simon@rabbitmq.com 3.3.0-1
- New Upstream Release
diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog
index c7d911350b..0b3a267d0f 100644
--- a/packaging/debs/Debian/debian/changelog
+++ b/packaging/debs/Debian/debian/changelog
@@ -1,3 +1,9 @@
+rabbitmq-server (3.3.1-1) unstable; urgency=low
+
+ * New Upstream Release
+
+ -- Simon MacMullen <simon@rabbitmq.com> Tue, 29 Apr 2014 11:49:23 +0100
+
rabbitmq-server (3.3.0-1) unstable; urgency=low
* New Upstream Release
diff --git a/src/gm.erl b/src/gm.erl
index 9a51bfc28f..0c0ac349b0 100644
--- a/src/gm.erl
+++ b/src/gm.erl
@@ -509,9 +509,9 @@ create_tables([]) ->
ok;
create_tables([{Table, Attributes} | Tables]) ->
case mnesia:create_table(Table, Attributes) of
- {atomic, ok} -> create_tables(Tables);
- {aborted, {already_exists, gm_group}} -> create_tables(Tables);
- Err -> Err
+ {atomic, ok} -> create_tables(Tables);
+ {aborted, {already_exists, Table}} -> create_tables(Tables);
+ Err -> Err
end.
table_definitions() ->
@@ -581,7 +581,11 @@ handle_call({confirmed_broadcast, Msg}, _From,
ok, State});
handle_call({confirmed_broadcast, Msg}, From, State) ->
- internal_broadcast(Msg, From, 0, State);
+ {Result, State1 = #state { pub_count = PubCount, confirms = Confirms }} =
+ internal_broadcast(Msg, 0, State),
+ Confirms1 = queue:in({PubCount, From}, Confirms),
+ handle_callback_result({Result, flush_broadcast_buffer(
+ State1 #state { confirms = Confirms1 })});
handle_call(info, _From,
State = #state { members_state = undefined }) ->
@@ -601,43 +605,27 @@ handle_call({add_on_right, _NewMember}, _From,
handle_call({add_on_right, NewMember}, _From,
State = #state { self = Self,
group_name = GroupName,
- view = View,
members_state = MembersState,
- module = Module,
- callback_args = Args,
txn_executor = TxnFun }) ->
- {MembersState1, Group} =
- record_new_member_in_group(
- GroupName, Self, NewMember,
- fun (Group1) ->
- View1 = group_to_view(Group1),
- MembersState1 = remove_erased_members(MembersState, View1),
- ok = send_right(NewMember, View1,
- {catchup, Self,
- prepare_members_state(MembersState1)}),
- MembersState1
- end, TxnFun),
- View2 = group_to_view(Group),
- State1 = check_neighbours(State #state { view = View2,
- members_state = MembersState1 }),
- Result = callback_view_changed(Args, Module, View, View2),
+ Group = record_new_member_in_group(NewMember, Self, GroupName, TxnFun),
+ View1 = group_to_view(Group),
+ MembersState1 = remove_erased_members(MembersState, View1),
+ ok = send_right(NewMember, View1,
+ {catchup, Self, prepare_members_state(MembersState1)}),
+ {Result, State1} = change_view(View1, State #state {
+ members_state = MembersState1 }),
handle_callback_result({Result, {ok, Group}, State1}).
-
handle_cast({?TAG, ReqVer, Msg},
State = #state { view = View,
members_state = MembersState,
- group_name = GroupName,
- module = Module,
- callback_args = Args }) ->
+ group_name = GroupName }) ->
{Result, State1} =
case needs_view_update(ReqVer, View) of
- true -> View1 = group_to_view(read_group(GroupName)),
+ true -> View1 = group_to_view(dirty_read_group(GroupName)),
MemberState1 = remove_erased_members(MembersState, View1),
- {callback_view_changed(Args, Module, View, View1),
- check_neighbours(
- State #state { view = View1,
- members_state = MemberState1 })};
+ change_view(View1, State #state {
+ members_state = MemberState1 });
false -> {ok, State}
end,
handle_callback_result(
@@ -657,7 +645,8 @@ handle_cast({broadcast, Msg, _SizeHint},
State});
handle_cast({broadcast, Msg, SizeHint}, State) ->
- internal_broadcast(Msg, none, SizeHint, State);
+ {Result, State1} = internal_broadcast(Msg, SizeHint, State),
+ handle_callback_result({Result, maybe_flush_broadcast_buffer(State1)});
handle_cast(join, State = #state { self = Self,
group_name = GroupName,
@@ -720,13 +709,15 @@ handle_info({'DOWN', MRef, process, _Pid, Reason},
_ ->
View1 = group_to_view(record_dead_member_in_group(
Member, GroupName, TxnFun)),
- State1 = case alive_view_members(View1) of
- [Self] -> State #state {
- members_state = blank_member_state(),
- confirms = purge_confirms(Confirms) };
- _ -> State
- end,
- handle_callback_result(maybe_erase_aliases(State1, View1))
+ handle_callback_result(
+ case alive_view_members(View1) of
+ [Self] -> maybe_erase_aliases(
+ State #state {
+ members_state = blank_member_state(),
+ confirms = purge_confirms(Confirms) },
+ View1);
+ _ -> change_view(View1, State)
+ end)
end.
@@ -876,30 +867,18 @@ ensure_broadcast_timer(State = #state { broadcast_timer = undefined }) ->
ensure_broadcast_timer(State) ->
State.
-internal_broadcast(Msg, From, SizeHint,
+internal_broadcast(Msg, SizeHint,
State = #state { self = Self,
pub_count = PubCount,
module = Module,
- confirms = Confirms,
callback_args = Args,
broadcast_buffer = Buffer,
broadcast_buffer_sz = BufferSize }) ->
PubCount1 = PubCount + 1,
- Result = Module:handle_msg(Args, get_pid(Self), Msg),
- Buffer1 = [{PubCount1, Msg} | Buffer],
- Confirms1 = case From of
- none -> Confirms;
- _ -> queue:in({PubCount1, From}, Confirms)
- end,
- State1 = State #state { pub_count = PubCount1,
- confirms = Confirms1,
- broadcast_buffer = Buffer1,
- broadcast_buffer_sz = BufferSize + SizeHint},
- handle_callback_result(
- {Result, case From of
- none -> maybe_flush_broadcast_buffer(State1);
- _ -> flush_broadcast_buffer(State1)
- end}).
+ {Module:handle_msg(Args, get_pid(Self), Msg),
+ State #state { pub_count = PubCount1,
+ broadcast_buffer = [{PubCount1, Msg} | Buffer],
+ broadcast_buffer_sz = BufferSize + SizeHint}}.
%% The Erlang distribution mechanism has an interesting quirk - it
%% will kill the VM cold with "Absurdly large distribution output data
@@ -1042,7 +1021,7 @@ ensure_alive_suffix1(MembersQ) ->
%% ---------------------------------------------------------------------------
join_group(Self, GroupName, TxnFun) ->
- join_group(Self, GroupName, read_group(GroupName), TxnFun).
+ join_group(Self, GroupName, dirty_read_group(GroupName), TxnFun).
join_group(Self, GroupName, {error, not_found}, TxnFun) ->
join_group(Self, GroupName,
@@ -1085,93 +1064,82 @@ join_group(Self, GroupName, #gm_group { members = Members } = Group, TxnFun) ->
end
end.
-read_group(GroupName) ->
+dirty_read_group(GroupName) ->
case mnesia:dirty_read(?GROUP_TABLE, GroupName) of
[] -> {error, not_found};
[Group] -> Group
end.
+read_group(GroupName) ->
+ case mnesia:read({?GROUP_TABLE, GroupName}) of
+ [] -> {error, not_found};
+ [Group] -> Group
+ end.
+
+write_group(Group) -> mnesia:write(?GROUP_TABLE, Group, write), Group.
+
prune_or_create_group(Self, GroupName, TxnFun) ->
- Group = TxnFun(
- fun () ->
- GroupNew = #gm_group { name = GroupName,
- members = [Self],
- version = get_version(Self) },
- case mnesia:read({?GROUP_TABLE, GroupName}) of
- [] ->
- mnesia:write(GroupNew),
- GroupNew;
- [Group1 = #gm_group { members = Members }] ->
- case lists:any(fun is_member_alive/1, Members) of
- true -> Group1;
- false -> mnesia:write(GroupNew),
- GroupNew
- end
+ TxnFun(
+ fun () ->
+ GroupNew = #gm_group { name = GroupName,
+ members = [Self],
+ version = get_version(Self) },
+ case read_group(GroupName) of
+ {error, not_found} ->
+ write_group(GroupNew);
+ Group = #gm_group { members = Members } ->
+ case lists:any(fun is_member_alive/1, Members) of
+ true -> Group;
+ false -> write_group(GroupNew)
end
- end),
- Group.
+ end
+ end).
record_dead_member_in_group(Member, GroupName, TxnFun) ->
- Group =
- TxnFun(
- fun () -> [Group1 = #gm_group { members = Members, version = Ver }] =
- mnesia:read({?GROUP_TABLE, GroupName}),
- case lists:splitwith(
- fun (Member1) -> Member1 =/= Member end, Members) of
- {_Members1, []} -> %% not found - already recorded dead
- Group1;
- {Members1, [Member | Members2]} ->
- Members3 = Members1 ++ [{dead, Member} | Members2],
- Group2 = Group1 #gm_group { members = Members3,
- version = Ver + 1 },
- mnesia:write(Group2),
- Group2
- end
- end),
- Group.
-
-record_new_member_in_group(GroupName, Left, NewMember, Fun, TxnFun) ->
- {Result, Group} =
- TxnFun(
- fun () ->
- [#gm_group { members = Members, version = Ver } = Group1] =
- mnesia:read({?GROUP_TABLE, GroupName}),
- {Prefix, [Left | Suffix]} =
- lists:splitwith(fun (M) -> M =/= Left end, Members),
- Members1 = Prefix ++ [Left, NewMember | Suffix],
- Group2 = Group1 #gm_group { members = Members1,
- version = Ver + 1 },
- Result = Fun(Group2),
- mnesia:write(Group2),
- {Result, Group2}
- end),
- {Result, Group}.
+ TxnFun(
+ fun () ->
+ Group = #gm_group { members = Members, version = Ver } =
+ read_group(GroupName),
+ case lists:splitwith(
+ fun (Member1) -> Member1 =/= Member end, Members) of
+ {_Members1, []} -> %% not found - already recorded dead
+ Group;
+ {Members1, [Member | Members2]} ->
+ Members3 = Members1 ++ [{dead, Member} | Members2],
+ write_group(Group #gm_group { members = Members3,
+ version = Ver + 1 })
+ end
+ end).
+
+record_new_member_in_group(NewMember, Left, GroupName, TxnFun) ->
+ TxnFun(
+ fun () ->
+ Group = #gm_group { members = Members, version = Ver } =
+ read_group(GroupName),
+ {Prefix, [Left | Suffix]} =
+ lists:splitwith(fun (M) -> M =/= Left end, Members),
+ write_group(Group #gm_group {
+ members = Prefix ++ [Left, NewMember | Suffix],
+ version = Ver + 1 })
+ end).
erase_members_in_group(Members, GroupName, TxnFun) ->
DeadMembers = [{dead, Id} || Id <- Members],
- Group =
- TxnFun(
- fun () ->
- [Group1 = #gm_group { members = [_|_] = Members1,
- version = Ver }] =
- mnesia:read({?GROUP_TABLE, GroupName}),
- case Members1 -- DeadMembers of
- Members1 -> Group1;
- Members2 -> Group2 =
- Group1 #gm_group { members = Members2,
- version = Ver + 1 },
- mnesia:write(Group2),
- Group2
- end
- end),
- Group.
+ TxnFun(
+ fun () ->
+ Group = #gm_group { members = [_|_] = Members1, version = Ver } =
+ read_group(GroupName),
+ case Members1 -- DeadMembers of
+ Members1 -> Group;
+ Members2 -> write_group(
+ Group #gm_group { members = Members2,
+ version = Ver + 1 })
+ end
+ end).
maybe_erase_aliases(State = #state { self = Self,
group_name = GroupName,
- view = View0,
members_state = MembersState,
- module = Module,
- callback_args = Args,
txn_executor = TxnFun }, View) ->
#view_member { aliases = Aliases } = fetch_view_member(Self, View),
{Erasable, MembersState1}
@@ -1190,9 +1158,7 @@ maybe_erase_aliases(State = #state { self = Self,
_ -> group_to_view(
erase_members_in_group(Erasable, GroupName, TxnFun))
end,
- State1 = State #state { members_state = MembersState1, view = View1 },
- {callback_view_changed(Args, Module, View0, View1),
- check_neighbours(State1)}.
+ change_view(View1, State #state { members_state = MembersState1 }).
can_erase_view_member(Self, Self, _LA, _LP) -> false;
can_erase_view_member(_Self, _Id, N, N) -> true;
@@ -1326,7 +1292,7 @@ prepare_members_state(MembersState) -> ?DICT:to_list(MembersState).
build_members_state(MembersStateList) -> ?DICT:from_list(MembersStateList).
make_member(GroupName) ->
- {case read_group(GroupName) of
+ {case dirty_read_group(GroupName) of
#gm_group { version = Version } -> Version;
{error, not_found} -> ?VERSION_START
end, self()}.
@@ -1390,16 +1356,19 @@ callback(Args, Module, Activity) ->
{stop, _Reason} = Error -> Error
end.
-callback_view_changed(Args, Module, OldView, NewView) ->
- OldMembers = all_known_members(OldView),
- NewMembers = all_known_members(NewView),
+change_view(View, State = #state { view = View0,
+ module = Module,
+ callback_args = Args }) ->
+ OldMembers = all_known_members(View0),
+ NewMembers = all_known_members(View),
Births = NewMembers -- OldMembers,
Deaths = OldMembers -- NewMembers,
- case {Births, Deaths} of
- {[], []} -> ok;
- _ -> Module:members_changed(
- Args, get_pids(Births), get_pids(Deaths))
- end.
+ Result = case {Births, Deaths} of
+ {[], []} -> ok;
+ _ -> Module:members_changed(
+ Args, get_pids(Births), get_pids(Deaths))
+ end,
+ {Result, check_neighbours(State #state { view = View })}.
handle_callback_result({Result, State}) ->
if_callback_success(
diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl
index 256543de61..b0f092a9a0 100644
--- a/src/rabbit_mirror_queue_misc.erl
+++ b/src/rabbit_mirror_queue_misc.erl
@@ -21,7 +21,8 @@
report_deaths/4, store_updated_slaves/1,
initial_queue_node/2, suggested_queue_nodes/1,
is_mirrored/1, update_mirrors/2, validate_policy/1,
- maybe_auto_sync/1, log_info/3, log_warning/3]).
+ maybe_auto_sync/1, maybe_drop_master_after_sync/1,
+ log_info/3, log_warning/3]).
%% for testing only
-export([module/1]).
@@ -57,6 +58,7 @@
-spec(is_mirrored/1 :: (rabbit_types:amqqueue()) -> boolean()).
-spec(update_mirrors/2 ::
(rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok').
+-spec(maybe_drop_master_after_sync/1 :: (rabbit_types:amqqueue()) -> 'ok').
-spec(maybe_auto_sync/1 :: (rabbit_types:amqqueue()) -> 'ok').
-spec(log_info/3 :: (rabbit_amqqueue:name(), string(), [any()]) -> 'ok').
-spec(log_warning/3 :: (rabbit_amqqueue:name(), string(), [any()]) -> 'ok').
@@ -346,6 +348,26 @@ update_mirrors0(OldQ = #amqqueue{name = QName},
maybe_auto_sync(NewQ),
ok.
+%% The arrival of a newly synced slave may cause the master to die if
+%% the policy does not want the master but it has been kept alive
+%% because there were no synced slaves.
+%%
+%% We don't just call update_mirrors/2 here since that could decide to
+%% start a slave for some other reason, and since we are the slave ATM
+%% that allows complicated deadlocks.
+maybe_drop_master_after_sync(Q = #amqqueue{name = QName,
+ pid = MPid}) ->
+ {DesiredMNode, DesiredSNodes} = suggested_queue_nodes(Q),
+ case node(MPid) of
+ DesiredMNode -> ok;
+ OldMNode -> false = lists:member(OldMNode, DesiredSNodes), %% [0]
+ drop_mirror(QName, OldMNode)
+ end,
+ ok.
+%% [0] ASSERTION - if the policy wants the master to change, it has
+%% not just shuffled it into the slaves. All our modes ensure this
+%% does not happen, but we should guard against a misbehaving plugin.
+
%%----------------------------------------------------------------------------
validate_policy(KeyList) ->
diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl
index f6acd91aa2..ee889f8442 100644
--- a/src/rabbit_mirror_queue_slave.erl
+++ b/src/rabbit_mirror_queue_slave.erl
@@ -922,8 +922,6 @@ update_ram_duration(BQ, BQS) ->
rabbit_memory_monitor:report_ram_duration(self(), RamDuration),
BQ:set_ram_duration_target(DesiredDuration, BQS1).
-%% [1] - the arrival of this newly synced slave may cause the master to die if
-%% the admin has requested a migration-type change to policy.
record_synchronised(#amqqueue { name = QName }) ->
Self = self(),
case rabbit_misc:execute_mnesia_transaction(
@@ -934,9 +932,9 @@ record_synchronised(#amqqueue { name = QName }) ->
[Q1 = #amqqueue { sync_slave_pids = SSPids }] ->
Q2 = Q1#amqqueue{sync_slave_pids = [Self | SSPids]},
rabbit_mirror_queue_misc:store_updated_slaves(Q2),
- {ok, Q1, Q2}
+ {ok, Q2}
end
end) of
- ok -> ok;
- {ok, Q1, Q2} -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2) %% [1]
+ ok -> ok;
+ {ok, Q} -> rabbit_mirror_queue_misc:maybe_drop_master_after_sync(Q)
end.
diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl
index 9d8606be83..7f7fcc3126 100644
--- a/src/rabbit_nodes.erl
+++ b/src/rabbit_nodes.erl
@@ -81,10 +81,12 @@ diagnostics_node(Node) ->
[{" * unable to connect to epmd (port ~s) on ~s: ~s~n",
[epmd_port(), Host, rabbit_misc:format_inet_error(Reason)]}];
{ok, NamePorts} ->
- case net_adm:ping(Node) of
- pong -> dist_working_diagnostics(Node);
- pang -> dist_broken_diagnostics(Name, Host, NamePorts)
- end
+ [{" * connected to epmd (port ~s) on ~s",
+ [epmd_port(), Host]}] ++
+ case net_adm:ping(Node) of
+ pong -> dist_working_diagnostics(Node);
+ pang -> dist_broken_diagnostics(Name, Host, NamePorts)
+ end
end].
epmd_port() ->
@@ -95,11 +97,11 @@ epmd_port() ->
dist_working_diagnostics(Node) ->
case rabbit:is_running(Node) of
- true -> [{" * node up, rabbit application running~n", []}];
- false -> [{" * node up, rabbit application not running~n"
+ true -> [{" * node ~s up, 'rabbit' application running", [Node]}];
+ false -> [{" * node ~s up, 'rabbit' application not running~n"
" * running applications on ~s: ~p~n"
- " * suggestion: start_app on ~s~n",
- [Node, remote_apps(Node), Node]}]
+ " * suggestion: start_app on ~s",
+ [Node, Node, remote_apps(Node), Node]}]
end.
remote_apps(Node) ->
@@ -119,13 +121,16 @@ dist_broken_diagnostics(Name, Host, NamePorts) ->
Host -> SelfName;
_ -> never_matches
end],
- [{" * ~s seems not to be running at all", [Name]} |
- case Others of
- [] -> [{" * no other nodes on ~s", [Host]}];
- _ -> [{" * other nodes on ~s: ~p", [Host, Others]}]
- end];
+ OthersDiag = case Others of
+ [] -> [{" no other nodes on ~s",
+ [Host]}];
+ _ -> [{" other nodes on ~s: ~p",
+ [Host, Others]}]
+ end,
+ [{" * epmd reports: node '~s' not running at all", [Name]},
+ OthersDiag, {" * suggestion: start the node", []}];
[{Name, Port}] ->
- [{" * found ~s (port ~b)", [Name, Port]} |
+ [{" * epmd reports node '~s' running on port ~b", [Name, Port]} |
case diagnose_connect(Host, Port) of
ok ->
[{" * TCP connection succeeded but Erlang distribution "
diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl
index 9e5cf2c030..ab95196d96 100644
--- a/src/rabbit_tests.erl
+++ b/src/rabbit_tests.erl
@@ -90,19 +90,9 @@ do_if_secondary_node(Up, Down) ->
setup_cluster() ->
do_if_secondary_node(
fun (SecondaryNode) ->
- cover:stop(SecondaryNode),
ok = control_action(stop_app, []),
- %% 'cover' does not cope at all well with nodes disconnecting,
- %% which happens as part of reset. So we turn it off
- %% temporarily. That is ok even if we're not in general using
- %% cover, it just turns the engine on / off and doesn't log
- %% anything. Note that this way cover won't be on when joining
- %% the cluster, but this is OK since we're testing the clustering
- %% interface elsewere anyway.
- cover:stop(nodes()),
ok = control_action(join_cluster,
[atom_to_list(SecondaryNode)]),
- cover:start(nodes()),
ok = control_action(start_app, []),
ok = control_action(start_app, SecondaryNode, [], [])
end,
diff --git a/src/truncate.erl b/src/truncate.erl
index 7113cfa4e3..02dba2e36a 100644
--- a/src/truncate.erl
+++ b/src/truncate.erl
@@ -44,18 +44,19 @@ report(List, Params) -> [case Item of
end || Item <- List].
term(Thing, {Content, Struct, ContentDec, StructDec}) ->
- term(Thing, #params{content = Content,
- struct = Struct,
- content_dec = ContentDec,
- struct_dec = StructDec});
+ term(Thing, true, #params{content = Content,
+ struct = Struct,
+ content_dec = ContentDec,
+ struct_dec = StructDec}).
-term(Bin, #params{content = N}) when (is_binary(Bin) orelse is_bitstring(Bin))
- andalso size(Bin) > N - ?ELLIPSIS_LENGTH ->
+term(Bin, _AllowPrintable, #params{content = N})
+ when (is_binary(Bin) orelse is_bitstring(Bin))
+ andalso size(Bin) > N - ?ELLIPSIS_LENGTH ->
Suffix = without_ellipsis(N),
<<Head:Suffix/binary, _/bitstring>> = Bin,
<<Head/binary, <<"...">>/binary>>;
-term(L, #params{struct = N} = Params) when is_list(L) ->
- case io_lib:printable_list(L) of
+term(L, AllowPrintable, #params{struct = N} = Params) when is_list(L) ->
+ case AllowPrintable andalso io_lib:printable_list(L) of
true -> N2 = without_ellipsis(N),
case length(L) > N2 of
true -> string:left(L, N2) ++ "...";
@@ -63,9 +64,9 @@ term(L, #params{struct = N} = Params) when is_list(L) ->
end;
false -> shrink_list(L, Params)
end;
-term(T, Params) when is_tuple(T) ->
+term(T, _AllowPrintable, Params) when is_tuple(T) ->
list_to_tuple(shrink_list(tuple_to_list(T), Params));
-term(T, _) ->
+term(T, _, _) ->
T.
without_ellipsis(N) -> erlang:max(N - ?ELLIPSIS_LENGTH, 0).
@@ -78,9 +79,9 @@ shrink_list([H|T], #params{content = Content,
struct = Struct,
content_dec = ContentDec,
struct_dec = StructDec} = Params) ->
- [term(H, Params#params{content = Content - ContentDec,
- struct = Struct - StructDec})
- | term(T, Params#params{struct = Struct - 1})].
+ [term(H, true, Params#params{content = Content - ContentDec,
+ struct = Struct - StructDec})
+ | term(T, false, Params#params{struct = Struct - 1})].
%%----------------------------------------------------------------------------
@@ -91,6 +92,7 @@ test() ->
test_short_examples_exactly() ->
F = fun (Term, Exp) -> Exp = term(Term, {10, 10, 5, 5}) end,
+ FSmall = fun (Term, Exp) -> Exp = term(Term, {2, 2, 2, 2}) end,
F([], []),
F("h", "h"),
F("hello world", "hello w..."),
@@ -101,6 +103,8 @@ test_short_examples_exactly() ->
F(<<1:1>>, <<1:1>>),
F(<<1:81>>, <<0:56, "...">>),
F({{{{a}}},{b},c,d,e,f,g,h,i,j,k}, {{{'...'}},{b},c,d,e,f,g,h,i,j,'...'}),
+ FSmall({a,30,40,40,40,40}, {a,30,'...'}),
+ FSmall([a,30,40,40,40,40], [a,30,'...']),
P = spawn(fun() -> receive die -> ok end end),
F([0, 0.0, <<1:1>>, F, P], [0, 0.0, <<1:1>>, F, P]),
P ! die,