27
27
handle_info /2 ,
28
28
terminate /2 ,
29
29
code_change /3 ]).
30
- -export ([register_consumer /5 ,
30
+ -export ([register_consumer /6 ,
31
31
unregister_consumer /5 ]).
32
32
33
33
-type vhost () :: binary ().
36
36
-type subscription_id () :: byte ().
37
37
38
38
-record (consumer ,
39
- {pid :: pid (), subscription_id :: subscription_id ()}).
40
- -record (group , {consumers :: [# consumer {}]}).
39
+ {pid :: pid (), subscription_id :: subscription_id (),
40
+ active :: boolean ()}).
41
+ -record (group ,
42
+ {consumers :: [# consumer {}], partition_index :: integer ()}).
41
43
-record (state ,
42
44
{groups :: #{{vhost (), stream (), consumer_name ()} => # group {}}}).
43
45
44
46
register_consumer (VirtualHost ,
45
47
Stream ,
48
+ PartitionIndex ,
46
49
ConsumerName ,
47
50
ConnectionPid ,
48
51
SubscriptionId ) ->
49
52
call ({register_consumer ,
50
53
VirtualHost ,
51
54
Stream ,
55
+ PartitionIndex ,
52
56
ConsumerName ,
53
57
ConnectionPid ,
54
58
SubscriptionId }).
@@ -121,23 +125,39 @@ init([]) ->
121
125
handle_call ({register_consumer ,
122
126
VirtualHost ,
123
127
Stream ,
128
+ PartitionIndex ,
124
129
ConsumerName ,
125
130
ConnectionPid ,
126
131
SubscriptionId },
127
132
_From , # state {groups = StreamGroups0 } = State ) ->
133
+ % % TODO monitor connection PID to remove consumers when their connection dies
134
+ % % this could require some index to avoid crawling the whole data structure
135
+ % % this is necessary to fail over to another consumer when one dies abruptly
136
+ % % also, check the liveliness of each consumer whenever there's a change in the group,
137
+ % % to make sure to get rid of zombies
138
+ % %
139
+ % % TODO monitor streams and virtual hosts as well
128
140
StreamGroups1 =
129
- maybe_create_group (VirtualHost , Stream , ConsumerName , StreamGroups0 ),
141
+ maybe_create_group (VirtualHost ,
142
+ Stream ,
143
+ PartitionIndex ,
144
+ ConsumerName ,
145
+ StreamGroups0 ),
130
146
Group0 =
131
147
lookup_group (VirtualHost , Stream , ConsumerName , StreamGroups1 ),
132
148
Consumer =
133
- # consumer {pid = ConnectionPid , subscription_id = SubscriptionId },
134
- Group = add_to_group (Consumer , Group0 ),
135
- Active = is_active (Consumer , Group ),
149
+ # consumer {pid = ConnectionPid ,
150
+ subscription_id = SubscriptionId ,
151
+ active = false },
152
+ Group1 = add_to_group (Consumer , Group0 ),
153
+ Active = compute_active_flag (Consumer , Group1 ),
154
+ # group {consumers = Consumers0 } = Group1 ,
155
+ Consumers1 = update_active_flag (Consumer , Active , Consumers0 ),
136
156
StreamGroups2 =
137
157
update_groups (VirtualHost ,
138
158
Stream ,
139
159
ConsumerName ,
140
- Group ,
160
+ Group1 # group { consumers = Consumers1 } ,
141
161
StreamGroups1 ),
142
162
{reply , {ok , Active }, State # state {groups = StreamGroups2 }};
143
163
handle_call ({unregister_consumer ,
@@ -164,32 +184,34 @@ handle_call({unregister_consumer,
164
184
{value , Consumer } ->
165
185
rabbit_log :debug (" Unregistering consumer ~p from group" ,
166
186
[Consumer ]),
167
- case lists : nth ( 1 , Consumers0 ) of
168
- Consumer ->
187
+ case Consumer of
188
+ # consumer { active = true } ->
169
189
rabbit_log :debug (" Unregistering the active consumer" ),
170
190
% % this is active one, remove it and notify the new active one if group not empty
171
191
Cs = lists :delete (Consumer , Consumers0 ),
172
192
case Cs of
173
193
[] ->
174
194
% % group is empty now
175
195
rabbit_log :debug (" Group is now empty" ),
176
- ok ;
196
+ Cs ;
177
197
_ ->
178
198
% % get new active one (the first) and notify it
199
+ NewActive = lists :nth (1 , Cs ),
179
200
# consumer {pid = Pid ,
180
201
subscription_id = SubId } =
181
- lists : nth ( 1 , Cs ) ,
202
+ NewActive ,
182
203
rabbit_log :debug (" New active consumer is ~p ~p " ,
183
204
[Pid , SubId ]),
184
205
Pid
185
206
! {sac ,
186
207
{{subscription_id , SubId },
187
- {active , true }}}
188
- end ,
189
- Cs ;
208
+ {active , true }}},
209
+ update_active_flag (NewActive , true ,
210
+ Cs )
211
+ end ;
190
212
_ActiveConsumer ->
191
- rabbit_log :debug (" Not the active consumer, just removing from the "
192
- " group" ),
213
+ rabbit_log :debug (" Not the active consumer, just removing it from "
214
+ " the group" ),
193
215
lists :delete (Consumer , Consumers0 )
194
216
end ;
195
217
error ->
@@ -212,13 +234,18 @@ handle_call({unregister_consumer,
212
234
handle_call (which_children , _From , State ) ->
213
235
{reply , [], State }.
214
236
215
- maybe_create_group (VirtualHost , Stream , ConsumerName , StreamGroups ) ->
237
+ maybe_create_group (VirtualHost ,
238
+ Stream ,
239
+ PartitionIndex ,
240
+ ConsumerName ,
241
+ StreamGroups ) ->
216
242
case StreamGroups of
217
243
#{{VirtualHost , Stream , ConsumerName } := _Group } ->
218
244
StreamGroups ;
219
245
SGS ->
220
246
maps :put ({VirtualHost , Stream , ConsumerName },
221
- # group {consumers = []}, SGS )
247
+ # group {consumers = [], partition_index = PartitionIndex },
248
+ SGS )
222
249
end .
223
250
224
251
lookup_group (VirtualHost , Stream , ConsumerName , StreamGroups ) ->
@@ -227,13 +254,23 @@ lookup_group(VirtualHost, Stream, ConsumerName, StreamGroups) ->
227
254
add_to_group (Consumer , # group {consumers = Consumers } = Group ) ->
228
255
Group # group {consumers = Consumers ++ [Consumer ]}.
229
256
230
- is_active (Consumer , # group {consumers = [Consumer ]}) ->
257
+ compute_active_flag (Consumer ,
258
+ # group {partition_index = - 1 , consumers = [Consumer ]}) ->
231
259
true ;
232
- is_active (Consumer , # group {consumers = [Consumer | _ ]}) ->
260
+ compute_active_flag (Consumer ,
261
+ # group {partition_index = - 1 , consumers = [Consumer | _ ]}) ->
233
262
true ;
234
- is_active (_ , _ ) ->
263
+ compute_active_flag (_ , _ ) ->
235
264
false .
236
265
266
+ update_active_flag (Consumer , Active , Consumers ) ->
267
+ lists :foldl (fun (C , Acc ) when C == Consumer ->
268
+ Acc ++ [Consumer # consumer {active = Active }];
269
+ (C , Acc ) ->
270
+ Acc ++ [C ]
271
+ end ,
272
+ [], Consumers ).
273
+
237
274
update_groups (VirtualHost ,
238
275
Stream ,
239
276
ConsumerName ,
0 commit comments