19
19
-export ([init /1 , handle_call /3 , handle_cast /2 , handle_info /2 , terminate /2 ,
20
20
code_change /3 ]).
21
21
22
+ -define (LARGE_CONSUMER_COUNT , 1000 ).
23
+
22
24
name (EventType ) ->
23
25
list_to_atom ((atom_to_list (EventType ) ++ " _metrics_gc" )).
24
26
@@ -42,7 +44,8 @@ handle_cast({event, #event{type = connection_closed, props = Props}},
42
44
handle_cast ({event , # event {type = channel_closed , props = Props }},
43
45
State = # state {basic_i = BIntervals }) ->
44
46
Pid = pget (pid , Props ),
45
- remove_channel (Pid , BIntervals ),
47
+ ConsumerCount = pget (consumer_count , Props ),
48
+ remove_channel (Pid , ConsumerCount , BIntervals ),
46
49
{noreply , State };
47
50
handle_cast ({event , # event {type = consumer_deleted , props = Props }}, State ) ->
48
51
remove_consumer (Props ),
@@ -82,13 +85,13 @@ remove_connection(Id, BIntervals) ->
82
85
delete_samples (connection_stats_coarse_conn_stats , Id , BIntervals ),
83
86
ok .
84
87
85
- remove_channel (Id , BIntervals ) ->
88
+ remove_channel (Id , ConsumerCount , BIntervals ) ->
86
89
ets :delete (channel_created_stats , Id ),
87
90
ets :delete (channel_stats , Id ),
88
91
delete_samples (channel_process_stats , Id , BIntervals ),
89
92
delete_samples (channel_stats_fine_stats , Id , BIntervals ),
90
93
delete_samples (channel_stats_deliver_stats , Id , BIntervals ),
91
- index_delete (consumer_stats , channel , Id ),
94
+ index_delete (consumer_stats , { channel , ConsumerCount } , Id ),
92
95
index_delete (channel_exchange_stats_fine_stats , channel , Id ),
93
96
index_delete (channel_queue_stats_deliver_stats , channel , Id ),
94
97
ok .
@@ -137,18 +140,32 @@ delete_samples(Table, Id, Intervals) ->
137
140
[ets :delete (Table , {Id , I }) || I <- Intervals ],
138
141
ok .
139
142
140
- index_delete (consumer_stats = Table , channel = Type , Id ) ->
141
- IndexTable = rabbit_mgmt_metrics_collector :index_table (Table , Type ),
142
- MatchPattern = {'_' , Id , '_' },
143
- % % Delete consumer_stats_queue_index
144
- ets :match_delete (consumer_stats_queue_index ,
145
- {'_' , MatchPattern }),
146
- % % Delete consumer_stats
147
- ets :match_delete (consumer_stats ,
148
- {MatchPattern ,'_' }),
149
- % % Delete consumer_stats_channel_index
150
- ets :delete (IndexTable , Id ),
151
- ok ;
143
+ index_delete (consumer_stats = Table , {channel = Type , ConsumerCount }, Id ) ->
144
+ % % This uses two different deletion strategies depending on how many
145
+ % % consumers a channel had. Most of the time there are many channels
146
+ % % with a few (or even just one) consumers. For this common case, `ets:delete/2` is optimal
147
+ % % since it avoids table scans.
148
+ % %
149
+ % % In the rather extreme scenario where only a handful of channels have a very large
150
+ % % (e.g. tens of thousands) of consumers, `ets:match_delete/2` becomes a more efficient option.
151
+ % %
152
+ % % See rabbitmq-server/rabbitmq#10451, rabbitmq-server/rabbitmq#9356.
153
+ case ConsumerCount > ? LARGE_CONSUMER_COUNT of
154
+ true ->
155
+ IndexTable = rabbit_mgmt_metrics_collector :index_table (Table , Type ),
156
+ MatchPattern = {'_' , Id , '_' },
157
+ % % Delete consumer_stats_queue_index
158
+ ets :match_delete (consumer_stats_queue_index ,
159
+ {'_' , MatchPattern }),
160
+ % % Delete consumer_stats
161
+ ets :match_delete (consumer_stats ,
162
+ {MatchPattern ,'_' }),
163
+ % % Delete consumer_stats_channel_index
164
+ ets :delete (IndexTable , Id ),
165
+ ok ;
166
+ false ->
167
+ index_delete (Table , Type , Id )
168
+ end ;
152
169
index_delete (Table , Type , Id ) ->
153
170
IndexTable = rabbit_mgmt_metrics_collector :index_table (Table , Type ),
154
171
Keys = ets :lookup (IndexTable , Id ),
0 commit comments