From eb18c78fcfcff9622a43e8823dee83f8348ca3c0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 15 Aug 2013 17:33:18 +0100 Subject: Make delete and unbind idempotent. --- src/rabbit_binding.erl | 41 +++++++++++++++++++++++++---------------- src/rabbit_channel.erl | 14 ++++++++++---- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 91f42e9c..98b427c7 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -152,7 +152,7 @@ exists(Binding) -> binding_action( Binding, fun (_Src, _Dst, B) -> rabbit_misc:const(mnesia:read({rabbit_route, B}) /= []) - end). + end, fun not_found_or_absent_errs/1). add(Binding) -> add(Binding, fun (_Src, _Dst) -> ok end). @@ -177,7 +177,7 @@ add(Binding, InnerFun) -> {error, _} = Err -> rabbit_misc:const(Err) end - end). + end, fun not_found_or_absent_errs/1). add(Src, Dst, B) -> [SrcDurable, DstDurable] = [durable(E) || E <- [Src, Dst]], @@ -200,14 +200,15 @@ remove(Binding, InnerFun) -> binding_action( Binding, fun (Src, Dst, B) -> - case mnesia:read(rabbit_route, B, write) of - [] -> rabbit_misc:const({error, binding_not_found}); - [_] -> case InnerFun(Src, Dst) of - ok -> remove(Src, Dst, B); - {error, _} = Err -> rabbit_misc:const(Err) - end + case mnesia:read(rabbit_route, B, write) =:= [] andalso + mnesia:read(rabbit_durable_route, B, write) =/= [] of + true -> rabbit_misc:const({error, binding_not_found}); + false -> case InnerFun(Src, Dst) of + ok -> remove(Src, Dst, B); + {error, _} = Err -> rabbit_misc:const(Err) + end end - end). + end, fun absent_errs_only/1). remove(Src, Dst, B) -> ok = sync_route(#route{binding = B}, durable(Src), durable(Dst), @@ -306,13 +307,13 @@ durable(#amqqueue{durable = D}) -> D. binding_action(Binding = #binding{source = SrcName, destination = DstName, - args = Arguments}, Fun) -> + args = Arguments}, Fun, ErrFun) -> call_with_source_and_destination( SrcName, DstName, fun (Src, Dst) -> SortedArgs = rabbit_misc:sort_field_table(Arguments), Fun(Src, Dst, Binding#binding{args = SortedArgs}) - end). + end, ErrFun). delete_object(Tab, Record, LockKind) -> %% this 'guarded' delete prevents unnecessary writes to the mnesia @@ -339,13 +340,9 @@ sync_transient_route(Route, Fun) -> ok = Fun(rabbit_route, Route, write), ok = Fun(rabbit_reverse_route, reverse_route(Route), write). -call_with_source_and_destination(SrcName, DstName, Fun) -> +call_with_source_and_destination(SrcName, DstName, Fun, ErrFun) -> SrcTable = table_for_resource(SrcName), DstTable = table_for_resource(DstName), - ErrFun = fun (Names) -> - Errs = [not_found_or_absent(Name) || Name <- Names], - rabbit_misc:const({error, {resources_missing, Errs}}) - end, rabbit_misc:execute_mnesia_tx_with_tail( fun () -> case {mnesia:read({SrcTable, SrcName}), @@ -357,6 +354,18 @@ call_with_source_and_destination(SrcName, DstName, Fun) -> end end). +not_found_or_absent_errs(Names) -> + Errs = [not_found_or_absent(Name) || Name <- Names], + rabbit_misc:const({error, {resources_missing, Errs}}). + +absent_errs_only(Names) -> + Errs = [E || Name <- Names, + {absent, _Q} = E <- [not_found_or_absent(Name)]], + rabbit_misc:const(case Errs of + [] -> ok; + _ -> {error, {resources_missing, Errs}} + end). + table_for_resource(#resource{kind = exchange}) -> rabbit_exchange; table_for_resource(#resource{kind = queue}) -> rabbit_queue. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d6c1e8c0..2cfbd96d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -934,7 +934,7 @@ handle_method(#'exchange.delete'{exchange = ExchangeNameBin, check_configure_permitted(ExchangeName, State), case rabbit_exchange:delete(ExchangeName, IfUnused) of {error, not_found} -> - rabbit_misc:not_found(ExchangeName); + return_ok(State, NoWait, #'exchange.delete_ok'{}); {error, in_use} -> precondition_failed("~s in use", [rabbit_misc:rs(ExchangeName)]); ok -> @@ -1047,9 +1047,15 @@ handle_method(#'queue.delete'{queue = QueueNameBin, _, State = #ch{conn_pid = ConnPid}) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), check_configure_permitted(QueueName, State), - case rabbit_amqqueue:with_exclusive_access_or_die( - QueueName, ConnPid, - fun (Q) -> rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) end) of + case rabbit_amqqueue:with( + QueueName, + fun (Q) -> + rabbit_amqqueue:check_exclusive_access(Q, ConnPid), + rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) + end, + fun (not_found) -> {ok, 0}; + ({absent, Q}) -> rabbit_misc:absent(Q) + end) of {error, in_use} -> precondition_failed("~s in use", [rabbit_misc:rs(QueueName)]); {error, not_empty} -> -- cgit v1.2.1