@@ -277,6 +277,7 @@ has_for_source(SrcName) ->
277277 contains (rabbit_semi_durable_route , Match ).
278278
279279remove_for_source (SrcName ) ->
280+ lock_route_tables (),
280281 Match = # route {binding = # binding {source = SrcName , _ = '_' }},
281282 Routes = lists :usort (
282283 mnesia :match_object (rabbit_route , Match , write ) ++
@@ -351,7 +352,28 @@ continue('$end_of_table') -> false;
351352continue ({[_ |_ ], _ }) -> true ;
352353continue ({[], Continuation }) -> continue (mnesia :select (Continuation )).
353354
355+ % % For bulk operations we lock the tables we are operating on in order
356+ % % to reduce the time complexity. Without the table locks we end up
357+ % % with num_tables*num_bulk_bindings row-level locks. Takiing each
358+ % % lock takes time proportional to the number of existing locks, thus
359+ % % resulting in O(num_bulk_bindings^2) complexity.
360+ % %
361+ % % The locks need to be write locks since ultimately we end up
362+ % % removing all these rows.
363+ % %
364+ % % The downside of all this is that no other binding operations except
365+ % % lookup/routing (which uses dirty ops) can take place
366+ % % concurrently. However, that is the case already since the bulk
367+ % % operations involve mnesia:match_object calls with a partial key,
368+ % % which entails taking a table lock.
369+ lock_route_tables () ->
370+ [mnesia :lock ({table , T }, write ) || T <- [rabbit_route ,
371+ rabbit_reverse_route ,
372+ rabbit_semi_durable_route ,
373+ rabbit_durable_route ]].
374+
354375remove_for_destination (DstName , DeleteFun ) ->
376+ lock_route_tables (),
355377 Match = reverse_route (
356378 # route {binding = # binding {destination = DstName , _ = '_' }}),
357379 ReverseRoutes = mnesia :match_object (rabbit_reverse_route , Match , write ),
0 commit comments