|
| 1 | +-module(rabbit_queue_commands). |
| 2 | + |
| 3 | +-export([add_member/5, |
| 4 | + list_with_local_promotable/1, |
| 5 | + delete_member/3, |
| 6 | + grow/5, |
| 7 | + peek/2, |
| 8 | + status/1, |
| 9 | + reclaim_memory/2, |
| 10 | + shrink_all/2]). |
| 11 | + |
| 12 | +-include_lib("rabbit_common/include/resource.hrl"). |
| 13 | + |
| 14 | +%% TODO: membership is a subset of ra_membership() with 'unknown' removed. |
| 15 | +-type membership() :: voter | non_voter | promotable. |
| 16 | + |
| 17 | +-callback add_member(VHost :: rabbit_types:vhost(), Name :: resource_name(), Node :: node(), Membership :: membership(), Timeout :: timeout()) -> ok | {error, term()}. |
| 18 | + |
| 19 | +-callback list_with_local_promotable() -> [amqqueue:amqqueue()] | {error, term()}. |
| 20 | + |
| 21 | +-callback delete_member(VHost :: rabbit_types:vhost(), Name :: resource_name(), Node :: node()) -> ok | {error, term()}. |
| 22 | + |
| 23 | +-callback peek(Pos :: non_neg_integer(), QName :: rabbit_amqqueue:name()) -> {ok, [{binary(), term()}]} | {error, term()}. |
| 24 | + |
| 25 | +-callback status(QName :: rabbit_amqqueue:name()) -> [[{binary(), term()}]] | {error, term()}. |
| 26 | + |
| 27 | +-callback reclaim_memory(QName :: rabbit_amqqueue:name()) -> ok | {error, term()}. |
| 28 | + |
| 29 | +-callback shrink_all(Node :: node()) -> [{rabbit_amqqueue:name(), |
| 30 | + {ok, pos_integer()} | {error, pos_integer(), term()}}] | {error, term()}. |
| 31 | + |
| 32 | +-spec add_member(rabbit_types:vhost(), resource_name(), node(), membership(), timeout()) -> ok | {error, term()}. |
| 33 | +add_member(VHost, Name, Node, Membership, Timeout) -> |
| 34 | + case rabbit_amqqueue:lookup(rabbit_misc:queue_resource(VHost, Name)) of |
| 35 | + {ok, Q} -> |
| 36 | + Type = amqqueue:get_type(Q), |
| 37 | + Type:add_member(VHost, Name, Node, Membership, Timeout); |
| 38 | + {error, not_found} = E -> |
| 39 | + E |
| 40 | + end. |
| 41 | + |
| 42 | +-spec list_with_local_promotable(atom() | binary()) -> [amqqueue:amqqueue()] | {error, term()}. |
| 43 | +list_with_local_promotable(TypeDescriptor) -> |
| 44 | + case rabbit_queue_type:lookup(TypeDescriptor) of |
| 45 | + {ok, Type} -> |
| 46 | + {ok, Type:list_with_local_promotable()}; |
| 47 | + {error, not_found} -> |
| 48 | + {error, {unknown_queue_type, TypeDescriptor}} |
| 49 | + end. |
| 50 | + |
| 51 | +-spec delete_member(rabbit_types:vhost(), resource_name(), node()) -> ok | {error, term()}. |
| 52 | +delete_member(VHost, Name, Node) -> |
| 53 | + case rabbit_amqqueue:lookup(rabbit_misc:queue_resource(VHost, Name)) of |
| 54 | + {ok, Q} -> |
| 55 | + Type = amqqueue:get_type(Q), |
| 56 | + Type:delete_member(VHost, Name, Node); |
| 57 | + {error, not_found} = E -> |
| 58 | + E |
| 59 | + end. |
| 60 | + |
| 61 | +-spec grow(node(), binary(), binary(), all | even, membership()) -> |
| 62 | + [{rabbit_amqqueue:name(), |
| 63 | + {ok, pos_integer()} | {error, pos_integer(), term()}}]. |
| 64 | +grow(Node, VhostSpec, QueueSpec, Strategy, Membership) -> |
| 65 | + Running = rabbit_nodes:list_running(), |
| 66 | + [begin |
| 67 | + Size = length(amqqueue:get_nodes(Q)), |
| 68 | + QName = amqqueue:get_name(Q), |
| 69 | + rabbit_log:info("~ts: adding a new member (replica) on node ~w", |
| 70 | + [rabbit_misc:rs(QName), Node]), |
| 71 | + Type = amqqueue:get_type(Q), |
| 72 | + case Type:add_member(Q, Node, Membership) of |
| 73 | + ok -> |
| 74 | + {QName, {ok, Size + 1}}; |
| 75 | + {error, Err} -> |
| 76 | + rabbit_log:warning( |
| 77 | + "~ts: failed to add member (replica) on node ~w, error: ~w", |
| 78 | + [rabbit_misc:rs(QName), Node, Err]), |
| 79 | + {QName, {error, Size, Err}} |
| 80 | + end |
| 81 | + end |
| 82 | + || Q <- rabbit_amqqueue:list(), |
| 83 | + rabbit_queue_type:is_replicable(Q), |
| 84 | + %% don't add a member if there is already one on the node |
| 85 | + not lists:member(Node, amqqueue:get_nodes(Q)), |
| 86 | + %% node needs to be running |
| 87 | + lists:member(Node, Running), |
| 88 | + matches_strategy(Strategy, amqqueue:get_nodes(Q)), |
| 89 | + is_match(amqqueue:get_vhost(Q), VhostSpec) andalso |
| 90 | + is_match(get_resource_name(amqqueue:get_name(Q)), QueueSpec) ]. |
| 91 | + |
| 92 | +-spec peek(Pos :: non_neg_integer(), QName :: rabbit_amqqueue:name()) -> {ok, [{binary(), term()}]} | {error, term()}. |
| 93 | +peek(Pos, QName) -> |
| 94 | + case rabbit_amqqueue:lookup(QName) of |
| 95 | + {ok, Q} -> |
| 96 | + Type = amqqueue:get_type(Q), |
| 97 | + Type:peek(Pos, QName); |
| 98 | + {error, not_found} = E -> |
| 99 | + E |
| 100 | + end. |
| 101 | + |
| 102 | +-spec status(QName :: rabbit_amqqueue:name()) -> [[{binary(), term()}]] | {error, term()}. |
| 103 | +status(QName) -> |
| 104 | + case rabbit_amqqueue:lookup(QName) of |
| 105 | + {ok, Q} -> |
| 106 | + Type = amqqueue:get_type(Q), |
| 107 | + Type:status(QName); |
| 108 | + {error, not_found} = E -> |
| 109 | + E |
| 110 | + end. |
| 111 | + |
| 112 | +-spec reclaim_memory(rabbit_types:vhost(), Name :: rabbit_misc:resource_name()) -> ok | {error, term()}. |
| 113 | +reclaim_memory(VHost, QueueName) -> |
| 114 | + QName = #resource{virtual_host = VHost, name = QueueName, kind = queue}, |
| 115 | + case rabbit_amqqueue:lookup(QName) of |
| 116 | + {ok, Q} -> |
| 117 | + Type = amqqueue:get_type(Q), |
| 118 | + Type:reclaim_memory(QName); |
| 119 | + {error, not_found} = E -> |
| 120 | + E |
| 121 | + end. |
| 122 | + |
| 123 | +-spec shrink_all(Node :: node(), TypeDescriptor :: rabbit_registry:type_descriptor()) -> |
| 124 | + {ok, pos_integer()} | |
| 125 | + {error, pos_integer(), term()} | |
| 126 | + {error, {unknown_queue_type, rabbit_registry:type_descriptor()}} | |
| 127 | + {error, atom()}. %% to cover not_supported |
| 128 | +shrink_all(Node, <<"all">>) -> |
| 129 | + lists:flatten([Type:shrink_all(Node) || Type <- rabbit_queue_type:list_replicable()]); |
| 130 | +shrink_all(Node, TypeDescriptor) -> |
| 131 | + case rabbit_queue_type:lookup(TypeDescriptor) of |
| 132 | + {ok, Type} -> |
| 133 | + {ok, Type:shrink_all(Node)}; |
| 134 | + {error, not_found} -> |
| 135 | + {error, {unknown_queue_type, TypeDescriptor}} |
| 136 | + end. |
| 137 | + |
| 138 | +matches_strategy(all, _) -> true; |
| 139 | +matches_strategy(even, Members) -> |
| 140 | + length(Members) rem 2 == 0. |
| 141 | + |
| 142 | +is_match(Subj, E) -> |
| 143 | + nomatch /= re:run(Subj, E). |
| 144 | + |
| 145 | +get_resource_name(#resource{name = Name}) -> |
| 146 | + Name. |
0 commit comments