Skip to content

Commit c8cda76

Browse files
Merge pull request #1589 from rabbitmq/exclusive-queues-cleanup-optimisation
Do not set table-wide and partial locks when deleting bindings.
2 parents 92261a4 + 1709368 commit c8cda76

File tree

3 files changed

+45
-41
lines changed

3 files changed

+45
-41
lines changed

src/rabbit_amqqueue.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ internal_delete(QueueName, ActingUser) ->
988988
?INTERNAL_USER),
989989
fun() ->
990990
ok = T(),
991-
rabbit_core_metrics:queue_deleted(QueueName),
991+
rabbit_core_metrics:queue_deleted(QueueName),
992992
ok = rabbit_event:notify(queue_deleted,
993993
[{name, QueueName},
994994
{user_who_performed_action, ActingUser}])

src/rabbit_amqqueue_process.erl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,14 @@ terminate_delete(EmitStats, Reason,
311311
fun() -> emit_stats(State) end);
312312
true -> ok
313313
end,
314-
%% don't care if the internal delete doesn't return 'ok'.
315-
rabbit_amqqueue:internal_delete(QName, ActingUser),
314+
%% This try-catch block transforms throws to errors since throws are not
315+
%% logged.
316+
try
317+
%% don't care if the internal delete doesn't return 'ok'.
318+
rabbit_amqqueue:internal_delete(QName, ActingUser)
319+
catch
320+
{error, Reason} -> error(Reason)
321+
end,
316322
BQS1
317323
end.
318324

src/rabbit_binding.erl

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ add(Binding, InnerFun, ActingUser) ->
166166
fun (Src, Dst, B) ->
167167
case rabbit_exchange:validate_binding(Src, B) of
168168
ok ->
169+
lock_resource(Src),
170+
lock_resource(Dst),
169171
%% this argument is used to check queue exclusivity;
170172
%% in general, we want to fail on that in preference to
171173
%% anything else
@@ -184,6 +186,8 @@ add(Binding, InnerFun, ActingUser) ->
184186
end, fun not_found_or_absent_errs/1).
185187

186188
add(Src, Dst, B, ActingUser) ->
189+
lock_resource(Src),
190+
lock_resource(Dst),
187191
[SrcDurable, DstDurable] = [durable(E) || E <- [Src, Dst]],
188192
case (SrcDurable andalso DstDurable andalso
189193
mnesia:read({rabbit_durable_route, B}) =/= []) of
@@ -206,6 +210,8 @@ remove(Binding, InnerFun, ActingUser) ->
206210
binding_action(
207211
Binding,
208212
fun (Src, Dst, B) ->
213+
lock_resource(Src),
214+
lock_resource(Dst),
209215
case mnesia:read(rabbit_route, B, write) of
210216
[] -> case mnesia:read(rabbit_durable_route, B, write) of
211217
[] -> rabbit_misc:const(ok);
@@ -219,6 +225,8 @@ remove(Binding, InnerFun, ActingUser) ->
219225
end, fun absent_errs_only/1).
220226

221227
remove(Src, Dst, B, ActingUser) ->
228+
lock_resource(Src),
229+
lock_resource(Dst),
222230
ok = sync_route(#route{binding = B}, durable(Src), durable(Dst),
223231
fun mnesia:delete_object/3),
224232
Deletions = maybe_auto_delete(
@@ -303,12 +311,12 @@ has_for_source(SrcName) ->
303311
contains(rabbit_semi_durable_route, Match).
304312

305313
remove_for_source(SrcName) ->
306-
lock_route_tables(),
314+
lock_resource(SrcName),
307315
Match = #route{binding = #binding{source = SrcName, _ = '_'}},
308316
remove_routes(
309317
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))).
312320

313321
remove_for_destination(DstName, OnlyDurable) ->
314322
remove_for_destination(DstName, OnlyDurable, fun remove_routes/1).
@@ -331,8 +339,8 @@ binding_action(Binding = #binding{source = SrcName,
331339
Fun(Src, Dst, Binding#binding{args = SortedArgs})
332340
end, ErrFun).
333341

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).
336344

337345
sync_route(Route, true, true, Fun) ->
338346
ok = Fun(rabbit_durable_route, Route, write),
@@ -393,66 +401,56 @@ continue('$end_of_table') -> false;
393401
continue({[_|_], _}) -> true;
394402
continue({[], Continuation}) -> continue(mnesia:select(Continuation)).
395403

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-
416404
remove_routes(Routes) ->
417405
%% This partitioning allows us to suppress unnecessary delete
418406
%% operations on disk tables, which require an fsync.
419407
{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,
422410
Routes),
423411
%% Of course the destination might not really be durable but it's
424412
%% just as easy to try to delete it from the semi-durable table
425413
%% 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) ||
427415
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) ||
429417
R <- DiskRoutes],
430418
[R#route.binding || R <- Routes].
431419

432420
remove_transient_routes(Routes) ->
433421
[begin
434-
ok = sync_transient_route(R, fun delete_object/3),
422+
ok = sync_transient_route(R, fun dirty_delete_object/3),
435423
R#route.binding
436424
end || R <- Routes].
437425

438426
remove_for_destination(DstName, OnlyDurable, Fun) ->
439-
lock_route_tables(),
427+
lock_resource(DstName),
440428
MatchFwd = #route{binding = #binding{destination = DstName, _ = '_'}},
441429
MatchRev = reverse_route(MatchFwd),
442430
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)];
446435
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))
451440
end,
452441
Bindings = Fun(Routes),
453442
group_bindings_fold(fun maybe_auto_delete/4, new_deletions(),
454443
lists:keysort(#binding.source, Bindings), OnlyDurable).
455444

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+
456454
%% Requires that its input binding list is sorted in exchange-name
457455
%% order, so that the grouping of bindings (for passing to
458456
%% group_bindings_and_auto_delete1) works properly.

0 commit comments

Comments
 (0)