Add a classic queue observer_cli plugin
This specifically targets CQv2 and aims to identify where messages are located (Erlang/GS2 mailbox, memory/disk/buffer, pending acks and confirms). This commit also makes it easy to add more plugins in the future.
This commit is contained in:
parent
ab269cca1a
commit
c7e7179fd0
|
|
@ -251,6 +251,12 @@
|
|||
{requires, [core_initialized, recovery]},
|
||||
{enables, routing_ready}]}).
|
||||
|
||||
-rabbit_boot_step({rabbit_observer_cli,
|
||||
[{description, "Observer CLI configuration"},
|
||||
{mfa, {rabbit_observer_cli, init, []}},
|
||||
{requires, [core_initialized, recovery]},
|
||||
{enables, routing_ready}]}).
|
||||
|
||||
-rabbit_boot_step({pre_flight,
|
||||
[{description, "ready to communicate with peers and clients"},
|
||||
{requires, [core_initialized, recovery, routing_ready]}]}).
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
-export([erase/1, init/3, reset_state/1, recover/7,
|
||||
terminate/3, delete_and_terminate/1,
|
||||
publish/7, publish/8, ack/2, read/3]).
|
||||
info/1, publish/7, publish/8, ack/2, read/3]).
|
||||
|
||||
%% Recovery. Unlike other functions in this module, these
|
||||
%% apply to all queues all at once.
|
||||
|
|
@ -559,6 +559,14 @@ delete_and_terminate(State = #qi { dir = Dir,
|
|||
State#qi{ segments = #{},
|
||||
fds = #{} }.
|
||||
|
||||
-spec info(state()) -> [{atom(), integer()}].
|
||||
|
||||
info(#qi{ write_buffer = WriteBuffer, write_buffer_updates = NumUpdates }) ->
|
||||
[
|
||||
{qi_buffer_size, map_size(WriteBuffer)},
|
||||
{qi_buffer_num_up, NumUpdates}
|
||||
].
|
||||
|
||||
-spec publish(rabbit_types:msg_id(), rabbit_variable_queue:seq_id(),
|
||||
rabbit_variable_queue:msg_location(),
|
||||
rabbit_types:message_properties(), boolean(),
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
-module(rabbit_classic_queue_store_v2).
|
||||
|
||||
-export([init/1, terminate/1,
|
||||
-export([init/1, terminate/1, info/1,
|
||||
write/4, sync/1, read/3, read_many/2, check_msg_on_disk/3,
|
||||
remove/2, delete_segments/2]).
|
||||
|
||||
|
|
@ -136,6 +136,11 @@ maybe_close_fd(undefined) ->
|
|||
maybe_close_fd(Fd) ->
|
||||
ok = file:close(Fd).
|
||||
|
||||
-spec info(state()) -> [{atom(), non_neg_integer()}].
|
||||
|
||||
info(#qs{ write_buffer = WriteBuffer }) ->
|
||||
[{qs_buffer_size, map_size(WriteBuffer)}].
|
||||
|
||||
-spec write(rabbit_variable_queue:seq_id(), rabbit_types:basic_message(),
|
||||
rabbit_types:message_properties(), State)
|
||||
-> {msg_location(), State} when State::state().
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
%% This Source Code Form is subject to the terms of the Mozilla Public
|
||||
%% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
%%
|
||||
%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rabbit_observer_cli).
|
||||
|
||||
-export([init/0]).
|
||||
|
||||
init() ->
|
||||
application:set_env(observer_cli, plugins, [
|
||||
rabbit_observer_cli_classic_queues:plugin_info()
|
||||
]).
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
%% This Source Code Form is subject to the terms of the Mozilla Public
|
||||
%% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
%%
|
||||
%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rabbit_observer_cli_classic_queues).
|
||||
|
||||
-export([plugin_info/0]).
|
||||
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||
|
||||
-include_lib("rabbit_common/include/rabbit.hrl").
|
||||
|
||||
plugin_info() ->
|
||||
#{
|
||||
module => rabbit_observer_cli_classic_queues,
|
||||
title => "Classic",
|
||||
shortcut => "CQ",
|
||||
sort_column => 4
|
||||
}.
|
||||
|
||||
attributes(State) ->
|
||||
Content1 = "Location of messages in classic queues.",
|
||||
Content2 = "Q, MQ and GQ are Erlang messages (total, mailbox and GS2 queue).",
|
||||
Content3 = "mem/disk are AMQP messages in memory or on disk.",
|
||||
Content4 = "pa/cf are messages pending acks or confirms.",
|
||||
Content5 = "qib/qibu/qsb are index/store buffer sizes, with qib = AMQP messages + qibu (acks).",
|
||||
{[
|
||||
[#{content => Content1, width => 133}],
|
||||
[#{content => Content2, width => 133}],
|
||||
[#{content => Content3, width => 133}],
|
||||
[#{content => Content4, width => 133}],
|
||||
[#{content => Content5, width => 133}]
|
||||
], State}.
|
||||
|
||||
sheet_header() ->
|
||||
[
|
||||
#{title => "Pid", width => 12, shortcut => "P"},
|
||||
#{title => "Vhost", width => 10, shortcut => "V"},
|
||||
#{title => "Name", width => 26, shortcut => "N"},
|
||||
#{title => "", width => 5, shortcut => "Q"},
|
||||
#{title => "", width => 5, shortcut => "MQ"},
|
||||
#{title => "", width => 5, shortcut => "GQ"},
|
||||
#{title => "", width => 10, shortcut => "mem"},
|
||||
#{title => "", width => 10, shortcut => "disk"},
|
||||
#{title => "", width => 8, shortcut => "pa"},
|
||||
#{title => "", width => 8, shortcut => "cf"},
|
||||
#{title => "", width => 6, shortcut => "qib"},
|
||||
#{title => "", width => 6, shortcut => "qibu"},
|
||||
#{title => "", width => 6, shortcut => "qsb"}
|
||||
].
|
||||
|
||||
sheet_body(State) ->
|
||||
Body = [begin
|
||||
#resource{name = Name, virtual_host = Vhost} = amqqueue:get_name(Q),
|
||||
case rabbit_amqqueue:pid_of(Q) of
|
||||
{error, not_found} ->
|
||||
["dead", Vhost, unicode:characters_to_binary([Name, " (dead)"]),
|
||||
0,0,0,0,0,0,0,0,0,0];
|
||||
Pid ->
|
||||
case process_info(Pid, message_queue_len) of
|
||||
undefined ->
|
||||
[Pid, Vhost, unicode:characters_to_binary([Name, " (dead)"]),
|
||||
0,0,0,0,0,0,0,0,0,0];
|
||||
{_, MsgQ} ->
|
||||
GS2Q = rabbit_core_metrics:get_gen_server2_stats(Pid),
|
||||
Info = rabbit_amqqueue:info(Q),
|
||||
BQInfo = proplists:get_value(backing_queue_status, Info),
|
||||
[
|
||||
Pid, Vhost, Name,
|
||||
MsgQ + GS2Q, MsgQ, GS2Q,
|
||||
proplists:get_value(q3, BQInfo),
|
||||
element(3, proplists:get_value(delta, BQInfo)),
|
||||
proplists:get_value(num_pending_acks, BQInfo),
|
||||
proplists:get_value(num_unconfirmed, BQInfo),
|
||||
proplists:get_value(qi_buffer_size, BQInfo, 0),
|
||||
proplists:get_value(qi_buffer_num_up, BQInfo, 0),
|
||||
proplists:get_value(qs_buffer_size, BQInfo)
|
||||
]
|
||||
end
|
||||
end
|
||||
end || Q <- list_classic_queues()],
|
||||
{Body, State}.
|
||||
|
||||
%% This function gets all classic queues regardless of durable/exclusive status.
|
||||
list_classic_queues() ->
|
||||
{atomic, Qs} =
|
||||
mnesia:sync_transaction(
|
||||
fun () ->
|
||||
mnesia:match_object(rabbit_queue,
|
||||
amqqueue:pattern_match_on_type(rabbit_classic_queue),
|
||||
read)
|
||||
end),
|
||||
Qs.
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
-compile({inline, [segment_entry_count/0]}).
|
||||
|
||||
-export([erase/1, init/3, reset_state/1, recover/7,
|
||||
terminate/3, delete_and_terminate/1,
|
||||
terminate/3, delete_and_terminate/1, info/1,
|
||||
pre_publish/7, flush_pre_publish_cache/2,
|
||||
publish/7, publish/8, deliver/2, ack/2, sync/1, needs_sync/1, flush/1,
|
||||
read/3, next_segment_boundary/1, bounds/1, start/2, stop/1]).
|
||||
|
|
@ -358,6 +358,11 @@ delete_and_terminate(State) ->
|
|||
ok = rabbit_file:recursive_delete([Dir]),
|
||||
State1.
|
||||
|
||||
-spec info(qistate()) -> [].
|
||||
|
||||
%% No info is implemented for v1 at this time.
|
||||
info(_) -> [].
|
||||
|
||||
pre_publish(MsgOrId, SeqId, MsgProps, IsPersistent, IsDelivered, JournalSizeHint,
|
||||
State = #qistate{pre_publish_cache = PPC,
|
||||
delivered_cache = DC}) ->
|
||||
|
|
|
|||
|
|
@ -869,11 +869,17 @@ info(backing_queue_status, #vqstate {
|
|||
target_ram_count = TargetRamCount,
|
||||
next_seq_id = NextSeqId,
|
||||
next_deliver_seq_id = NextDeliverSeqId,
|
||||
ram_pending_ack = RPA,
|
||||
disk_pending_ack = DPA,
|
||||
unconfirmed = UC,
|
||||
unconfirmed_simple = UCS,
|
||||
index_mod = IndexMod,
|
||||
index_state = IndexState,
|
||||
store_state = StoreState,
|
||||
rates = #rates { in = AvgIngressRate,
|
||||
out = AvgEgressRate,
|
||||
ack_in = AvgAckIngressRate,
|
||||
ack_out = AvgAckEgressRate }}) ->
|
||||
|
||||
[ {mode , Mode},
|
||||
{version , Version},
|
||||
{q1 , 0},
|
||||
|
|
@ -885,10 +891,14 @@ info(backing_queue_status, #vqstate {
|
|||
{target_ram_count , TargetRamCount},
|
||||
{next_seq_id , NextSeqId},
|
||||
{next_deliver_seq_id , NextDeliverSeqId},
|
||||
{num_pending_acks , map_size(RPA) + map_size(DPA)},
|
||||
{num_unconfirmed , sets:size(UC) + sets:size(UCS)},
|
||||
{avg_ingress_rate , AvgIngressRate},
|
||||
{avg_egress_rate , AvgEgressRate},
|
||||
{avg_ack_ingress_rate, AvgAckIngressRate},
|
||||
{avg_ack_egress_rate , AvgAckEgressRate} ];
|
||||
{avg_ack_egress_rate , AvgAckEgressRate} ]
|
||||
++ IndexMod:info(IndexState)
|
||||
++ rabbit_classic_queue_store_v2:info(StoreState);
|
||||
info(_, _) ->
|
||||
''.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue