Skip to content

Commit 61f3fd1

Browse files
Merge pull request #10478 from rabbitmq/mergify/bp/v3.12.x/pr-10476
Rework consumer sample deletion when a channel is closed (backport #10476)
2 parents 24b0677 + 9e5565b commit 61f3fd1

File tree

2 files changed

+34
-16
lines changed

2 files changed

+34
-16
lines changed

deps/rabbit/src/rabbit_channel.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,8 @@ terminate(_Reason,
832832
end, CM),
833833
rabbit_core_metrics:channel_closed(self()),
834834
rabbit_event:notify(channel_closed, [{pid, self()},
835-
{user_who_performed_action, Username}]),
835+
{user_who_performed_action, Username},
836+
{consumer_count, maps:size(CM)}]),
836837
case rabbit_confirms:size(State#ch.unconfirmed) of
837838
0 -> ok;
838839
NumConfirms ->

deps/rabbitmq_management_agent/src/rabbit_mgmt_metrics_gc.erl

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
2020
code_change/3]).
2121

22+
-define(LARGE_CONSUMER_COUNT, 1000).
23+
2224
name(EventType) ->
2325
list_to_atom((atom_to_list(EventType) ++ "_metrics_gc")).
2426

@@ -42,7 +44,8 @@ handle_cast({event, #event{type = connection_closed, props = Props}},
4244
handle_cast({event, #event{type = channel_closed, props = Props}},
4345
State = #state{basic_i = BIntervals}) ->
4446
Pid = pget(pid, Props),
45-
remove_channel(Pid, BIntervals),
47+
ConsumerCount = pget(consumer_count, Props),
48+
remove_channel(Pid, ConsumerCount, BIntervals),
4649
{noreply, State};
4750
handle_cast({event, #event{type = consumer_deleted, props = Props}}, State) ->
4851
remove_consumer(Props),
@@ -82,13 +85,13 @@ remove_connection(Id, BIntervals) ->
8285
delete_samples(connection_stats_coarse_conn_stats, Id, BIntervals),
8386
ok.
8487

85-
remove_channel(Id, BIntervals) ->
88+
remove_channel(Id, ConsumerCount, BIntervals) ->
8689
ets:delete(channel_created_stats, Id),
8790
ets:delete(channel_stats, Id),
8891
delete_samples(channel_process_stats, Id, BIntervals),
8992
delete_samples(channel_stats_fine_stats, Id, BIntervals),
9093
delete_samples(channel_stats_deliver_stats, Id, BIntervals),
91-
index_delete(consumer_stats, channel, Id),
94+
index_delete(consumer_stats, {channel, ConsumerCount}, Id),
9295
index_delete(channel_exchange_stats_fine_stats, channel, Id),
9396
index_delete(channel_queue_stats_deliver_stats, channel, Id),
9497
ok.
@@ -136,18 +139,32 @@ delete_samples(Table, Id, Intervals) ->
136139
[ets:delete(Table, {Id, I}) || I <- Intervals],
137140
ok.
138141

139-
index_delete(consumer_stats = Table, channel = Type, Id) ->
140-
IndexTable = rabbit_mgmt_metrics_collector:index_table(Table, Type),
141-
MatchPattern = {'_', Id, '_'},
142-
%% Delete consumer_stats_queue_index
143-
ets:match_delete(consumer_stats_queue_index,
144-
{'_', MatchPattern}),
145-
%% Delete consumer_stats
146-
ets:match_delete(consumer_stats,
147-
{MatchPattern,'_'}),
148-
%% Delete consumer_stats_channel_index
149-
ets:delete(IndexTable, Id),
150-
ok;
142+
index_delete(consumer_stats = Table, {channel = Type, ConsumerCount}, Id) ->
143+
%% This uses two different deletion strategies depending on how many
144+
%% consumers a channel had. Most of the time there are many channels
145+
%% with a few (or even just one) consumers. For this common case, `ets:delete/2` is optimal
146+
%% since it avoids table scans.
147+
%%
148+
%% In the rather extreme scenario where only a handful of channels have a very large
149+
%% (e.g. tens of thousands) of consumers, `ets:match_delete/2` becomes a more efficient option.
150+
%%
151+
%% See rabbitmq-server/rabbitmq#10451, rabbitmq-server/rabbitmq#9356.
152+
case ConsumerCount > ?LARGE_CONSUMER_COUNT of
153+
true ->
154+
IndexTable = rabbit_mgmt_metrics_collector:index_table(Table, Type),
155+
MatchPattern = {'_', Id, '_'},
156+
%% Delete consumer_stats_queue_index
157+
ets:match_delete(consumer_stats_queue_index,
158+
{'_', MatchPattern}),
159+
%% Delete consumer_stats
160+
ets:match_delete(consumer_stats,
161+
{MatchPattern,'_'}),
162+
%% Delete consumer_stats_channel_index
163+
ets:delete(IndexTable, Id),
164+
ok;
165+
false ->
166+
index_delete(Table, Type, Id)
167+
end;
151168
index_delete(Table, Type, Id) ->
152169
IndexTable = rabbit_mgmt_metrics_collector:index_table(Table, Type),
153170
Keys = ets:lookup(IndexTable, Id),

0 commit comments

Comments
 (0)