@@ -166,6 +166,8 @@ add(Binding, InnerFun, ActingUser) ->
166
166
fun (Src , Dst , B ) ->
167
167
case rabbit_exchange :validate_binding (Src , B ) of
168
168
ok ->
169
+ lock_resource (Src ),
170
+ lock_resource (Dst ),
169
171
% % this argument is used to check queue exclusivity;
170
172
% % in general, we want to fail on that in preference to
171
173
% % anything else
@@ -184,6 +186,8 @@ add(Binding, InnerFun, ActingUser) ->
184
186
end , fun not_found_or_absent_errs /1 ).
185
187
186
188
add (Src , Dst , B , ActingUser ) ->
189
+ lock_resource (Src ),
190
+ lock_resource (Dst ),
187
191
[SrcDurable , DstDurable ] = [durable (E ) || E <- [Src , Dst ]],
188
192
case (SrcDurable andalso DstDurable andalso
189
193
mnesia :read ({rabbit_durable_route , B }) =/= []) of
@@ -206,6 +210,8 @@ remove(Binding, InnerFun, ActingUser) ->
206
210
binding_action (
207
211
Binding ,
208
212
fun (Src , Dst , B ) ->
213
+ lock_resource (Src ),
214
+ lock_resource (Dst ),
209
215
case mnesia :read (rabbit_route , B , write ) of
210
216
[] -> case mnesia :read (rabbit_durable_route , B , write ) of
211
217
[] -> rabbit_misc :const (ok );
@@ -219,6 +225,8 @@ remove(Binding, InnerFun, ActingUser) ->
219
225
end , fun absent_errs_only /1 ).
220
226
221
227
remove (Src , Dst , B , ActingUser ) ->
228
+ lock_resource (Src ),
229
+ lock_resource (Dst ),
222
230
ok = sync_route (# route {binding = B }, durable (Src ), durable (Dst ),
223
231
fun mnesia :delete_object /3 ),
224
232
Deletions = maybe_auto_delete (
@@ -303,12 +311,12 @@ has_for_source(SrcName) ->
303
311
contains (rabbit_semi_durable_route , Match ).
304
312
305
313
remove_for_source (SrcName ) ->
306
- lock_route_tables ( ),
314
+ lock_resource ( SrcName ),
307
315
Match = # route {binding = # binding {source = SrcName , _ = '_' }},
308
316
remove_routes (
309
317
lists :usort (
310
- mnesia :match_object (rabbit_route , Match , write ) ++
311
- mnesia :match_object (rabbit_semi_durable_route , Match , write ))).
318
+ mnesia :dirty_match_object (rabbit_route , Match ) ++
319
+ mnesia :dirty_match_object (rabbit_semi_durable_route , Match ))).
312
320
313
321
remove_for_destination (DstName , OnlyDurable ) ->
314
322
remove_for_destination (DstName , OnlyDurable , fun remove_routes /1 ).
@@ -331,8 +339,8 @@ binding_action(Binding = #binding{source = SrcName,
331
339
Fun (Src , Dst , Binding # binding {args = SortedArgs })
332
340
end , ErrFun ).
333
341
334
- delete_object (Table , Record , LockKind ) ->
335
- mnesia :delete_object (Table , Record , LockKind ).
342
+ dirty_delete_object (Table , Record , _LockKind ) ->
343
+ mnesia :dirty_delete_object (Table , Record ).
336
344
337
345
sync_route (Route , true , true , Fun ) ->
338
346
ok = Fun (rabbit_durable_route , Route , write ),
@@ -393,66 +401,56 @@ continue('$end_of_table') -> false;
393
401
continue ({[_ |_ ], _ }) -> true ;
394
402
continue ({[], Continuation }) -> continue (mnesia :select (Continuation )).
395
403
396
- % % For bulk operations we lock the tables we are operating on in order
397
- % % to reduce the time complexity. Without the table locks we end up
398
- % % with num_tables*num_bulk_bindings row-level locks. Taking each lock
399
- % % takes time proportional to the number of existing locks, thus
400
- % % resulting in O(num_bulk_bindings^2) complexity.
401
- % %
402
- % % The locks need to be write locks since ultimately we end up
403
- % % removing all these rows.
404
- % %
405
- % % The downside of all this is that no other binding operations except
406
- % % lookup/routing (which uses dirty ops) can take place
407
- % % concurrently. However, that is the case already since the bulk
408
- % % operations involve mnesia:match_object calls with a partial key,
409
- % % which entails taking a table lock.
410
- lock_route_tables () ->
411
- [mnesia :lock ({table , T }, write ) || T <- [rabbit_route ,
412
- rabbit_reverse_route ,
413
- rabbit_semi_durable_route ,
414
- rabbit_durable_route ]].
415
-
416
404
remove_routes (Routes ) ->
417
405
% % This partitioning allows us to suppress unnecessary delete
418
406
% % operations on disk tables, which require an fsync.
419
407
{RamRoutes , DiskRoutes } =
420
- lists :partition (fun (R ) -> mnesia :match_object (
421
- rabbit_durable_route , R , write ) == [] end ,
408
+ lists :partition (fun (R ) -> mnesia :dirty_match_object (
409
+ rabbit_durable_route , R ) == [] end ,
422
410
Routes ),
423
411
% % Of course the destination might not really be durable but it's
424
412
% % just as easy to try to delete it from the semi-durable table
425
413
% % than check first
426
- [ok = sync_route (R , false , true , fun mnesia : delete_object /3 ) ||
414
+ [ok = sync_route (R , false , true , fun dirty_delete_object /3 ) ||
427
415
R <- RamRoutes ],
428
- [ok = sync_route (R , true , true , fun mnesia : delete_object /3 ) ||
416
+ [ok = sync_route (R , true , true , fun dirty_delete_object /3 ) ||
429
417
R <- DiskRoutes ],
430
418
[R # route .binding || R <- Routes ].
431
419
432
420
remove_transient_routes (Routes ) ->
433
421
[begin
434
- ok = sync_transient_route (R , fun delete_object /3 ),
422
+ ok = sync_transient_route (R , fun dirty_delete_object /3 ),
435
423
R # route .binding
436
424
end || R <- Routes ].
437
425
438
426
remove_for_destination (DstName , OnlyDurable , Fun ) ->
439
- lock_route_tables ( ),
427
+ lock_resource ( DstName ),
440
428
MatchFwd = # route {binding = # binding {destination = DstName , _ = '_' }},
441
429
MatchRev = reverse_route (MatchFwd ),
442
430
Routes = case OnlyDurable of
443
- false -> [reverse_route (R ) ||
444
- R <- mnesia :match_object (
445
- rabbit_reverse_route , MatchRev , write )];
431
+ false ->
432
+ [reverse_route (R ) ||
433
+ R <- mnesia :dirty_match_object (
434
+ rabbit_reverse_route , MatchRev )];
446
435
true -> lists :usort (
447
- mnesia :match_object (
448
- rabbit_durable_route , MatchFwd , write ) ++
449
- mnesia :match_object (
450
- rabbit_semi_durable_route , MatchFwd , write ))
436
+ mnesia :dirty_match_object (
437
+ rabbit_durable_route , MatchFwd ) ++
438
+ mnesia :dirty_match_object (
439
+ rabbit_semi_durable_route , MatchFwd ))
451
440
end ,
452
441
Bindings = Fun (Routes ),
453
442
group_bindings_fold (fun maybe_auto_delete /4 , new_deletions (),
454
443
lists :keysort (# binding .source , Bindings ), OnlyDurable ).
455
444
445
+ % % Instead of locking entire table on remove operations we can lock the
446
+ % % affected resource only. This will allow us to use dirty_match_object for
447
+ % % do faster search of records to delete.
448
+ % % This works better when there are multiple resources deleted at once, for
449
+ % % example when exclusive queues are deleted.
450
+ lock_resource (Name ) ->
451
+ mnesia :lock ({global , Name , mnesia :table_info (rabbit_route , where_to_write )},
452
+ write ).
453
+
456
454
% % Requires that its input binding list is sorted in exchange-name
457
455
% % order, so that the grouping of bindings (for passing to
458
456
% % group_bindings_and_auto_delete1) works properly.
0 commit comments