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 .
@@ -136,18 +139,32 @@ delete_samples(Table, Id, Intervals) ->
136
139
[ets :delete (Table , {Id , I }) || I <- Intervals ],
137
140
ok .
138
141
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 ;
151
168
index_delete (Table , Type , Id ) ->
152
169
IndexTable = rabbit_mgmt_metrics_collector :index_table (Table , Type ),
153
170
Keys = ets :lookup (IndexTable , Id ),
0 commit comments