Merge pull request #9080 from rabbitmq/mqtt-qos0-queue-metrics
Add Prometheus metric for messages dropped by MQTT QoS 0 Queue
This commit is contained in:
		
						commit
						a6c5820e47
					
				|  | @ -1,2 +1,41 @@ | ||||||
| -define(NUM_PROTOCOL_COUNTERS, 8). | -define(NUM_PROTOCOL_COUNTERS, 8). | ||||||
| -define(NUM_PROTOCOL_QUEUE_TYPE, 8). | -define(NUM_PROTOCOL_QUEUE_TYPE_COUNTERS, 8). | ||||||
|  | 
 | ||||||
|  | %% Dead Letter counters: | ||||||
|  | %% | ||||||
|  | %% The following two counters are mutually exclusive because | ||||||
|  | %% quorum queue dead-letter-strategy at-least-once is incompatible with overflow drop-head. | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_MAXLEN, 1). | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_CONFIRMED, 1). | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_EXPIRED, 2). | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_REJECTED, 3). | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT, 4). | ||||||
|  | 
 | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, | ||||||
|  |         {messages_dead_lettered_maxlen_total, ?MESSAGES_DEAD_LETTERED_MAXLEN, counter, | ||||||
|  |          "Total number of messages dead-lettered due to overflow drop-head or reject-publish-dlx" | ||||||
|  |         }). | ||||||
|  | 
 | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_CONFIRMED_COUNTER, | ||||||
|  |         { | ||||||
|  |          messages_dead_lettered_confirmed_total, ?MESSAGES_DEAD_LETTERED_CONFIRMED, counter, | ||||||
|  |          "Total number of messages dead-lettered and confirmed by target queues" | ||||||
|  |         }). | ||||||
|  | 
 | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_EXPIRED_COUNTER, | ||||||
|  |         { | ||||||
|  |          messages_dead_lettered_expired_total, ?MESSAGES_DEAD_LETTERED_EXPIRED, counter, | ||||||
|  |          "Total number of messages dead-lettered due to message TTL exceeded" | ||||||
|  |         }). | ||||||
|  | 
 | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_REJECTED_COUNTER, | ||||||
|  |         { | ||||||
|  |          messages_dead_lettered_rejected_total, ?MESSAGES_DEAD_LETTERED_REJECTED, counter, | ||||||
|  |          "Total number of messages dead-lettered due to basic.reject or basic.nack" | ||||||
|  |         }). | ||||||
|  | 
 | ||||||
|  | -define(MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER, | ||||||
|  |         { | ||||||
|  |          messages_dead_lettered_delivery_limit_total, ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT, counter, | ||||||
|  |          "Total number of messages dead-lettered due to delivery-limit exceeded" | ||||||
|  |         }). | ||||||
|  |  | ||||||
|  | @ -7,6 +7,8 @@ | ||||||
| 
 | 
 | ||||||
| -module(rabbit_global_counters). | -module(rabbit_global_counters). | ||||||
| 
 | 
 | ||||||
|  | -include("rabbit_global_counters.hrl"). | ||||||
|  | 
 | ||||||
| -export([ | -export([ | ||||||
|          boot_step/0, |          boot_step/0, | ||||||
|          init/1, |          init/1, | ||||||
|  | @ -128,57 +130,48 @@ | ||||||
|                 } |                 } | ||||||
|             ]). |             ]). | ||||||
| 
 | 
 | ||||||
| -define(MESSAGES_DEAD_LETTERED_EXPIRED, 1). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_REJECTED, 2). |  | ||||||
| %% The following two counters are mutually exclusive because |  | ||||||
| %% quorum queue dead-letter-strategy at-least-once is incompatible with overflow drop-head. |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_MAXLEN, 3). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_CONFIRMED, 3). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT, 4). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_COUNTERS, |  | ||||||
|         [ |  | ||||||
|          { |  | ||||||
|           messages_dead_lettered_expired_total, ?MESSAGES_DEAD_LETTERED_EXPIRED, counter, |  | ||||||
|           "Total number of messages dead-lettered due to message TTL exceeded" |  | ||||||
|          }, |  | ||||||
|          { |  | ||||||
|           messages_dead_lettered_rejected_total, ?MESSAGES_DEAD_LETTERED_REJECTED, counter, |  | ||||||
|           "Total number of messages dead-lettered due to basic.reject or basic.nack" |  | ||||||
|          } |  | ||||||
|         ]). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, |  | ||||||
|         { |  | ||||||
|          messages_dead_lettered_maxlen_total, ?MESSAGES_DEAD_LETTERED_MAXLEN, counter, |  | ||||||
|          "Total number of messages dead-lettered due to overflow drop-head or reject-publish-dlx" |  | ||||||
|         }). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_CONFIRMED_COUNTER, |  | ||||||
|         { |  | ||||||
|          messages_dead_lettered_confirmed_total, ?MESSAGES_DEAD_LETTERED_CONFIRMED, counter, |  | ||||||
|          "Total number of messages dead-lettered and confirmed by target queues" |  | ||||||
|         }). |  | ||||||
| -define(MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER, |  | ||||||
|         { |  | ||||||
|          messages_dead_lettered_delivery_limit_total, ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT, counter, |  | ||||||
|          "Total number of messages dead-lettered due to delivery-limit exceeded" |  | ||||||
|         }). |  | ||||||
| 
 |  | ||||||
| boot_step() -> | boot_step() -> | ||||||
|  |     %% Protocol counters | ||||||
|     init([{protocol, amqp091}]), |     init([{protocol, amqp091}]), | ||||||
|  | 
 | ||||||
|  |     %% Protocol & Queue Type counters | ||||||
|     init([{protocol, amqp091}, {queue_type, rabbit_classic_queue}]), |     init([{protocol, amqp091}, {queue_type, rabbit_classic_queue}]), | ||||||
|     init([{protocol, amqp091}, {queue_type, rabbit_quorum_queue}]), |     init([{protocol, amqp091}, {queue_type, rabbit_quorum_queue}]), | ||||||
|     init([{protocol, amqp091}, {queue_type, rabbit_stream_queue}]), |     init([{protocol, amqp091}, {queue_type, rabbit_stream_queue}]), | ||||||
|  | 
 | ||||||
|  |     %% Dead Letter counters | ||||||
|  |     %% | ||||||
|  |     %% Streams never dead letter. | ||||||
|  |     %% | ||||||
|  |     %% Source classic queue dead letters. | ||||||
|     init([{queue_type, rabbit_classic_queue}, {dead_letter_strategy, disabled}], |     init([{queue_type, rabbit_classic_queue}, {dead_letter_strategy, disabled}], | ||||||
|          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER]), |          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_EXPIRED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_REJECTED_COUNTER]), | ||||||
|     init([{queue_type, rabbit_classic_queue}, {dead_letter_strategy, at_most_once}], |     init([{queue_type, rabbit_classic_queue}, {dead_letter_strategy, at_most_once}], | ||||||
|          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER]), |          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_EXPIRED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_REJECTED_COUNTER]), | ||||||
|  |     %% | ||||||
|  |     %% Source quorum queue dead letters. | ||||||
|  |     %% Only quorum queues can dead letter due to delivery-limit exceeded. | ||||||
|  |     %% Only quorum queues support dead letter strategy at-least-once. | ||||||
|     init([{queue_type, rabbit_quorum_queue}, {dead_letter_strategy, disabled}], |     init([{queue_type, rabbit_quorum_queue}, {dead_letter_strategy, disabled}], | ||||||
|          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, |          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, | ||||||
|           ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER]), |           ?MESSAGES_DEAD_LETTERED_EXPIRED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_REJECTED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER | ||||||
|  |          ]), | ||||||
|     init([{queue_type, rabbit_quorum_queue}, {dead_letter_strategy, at_most_once}], |     init([{queue_type, rabbit_quorum_queue}, {dead_letter_strategy, at_most_once}], | ||||||
|          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, |          [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER, | ||||||
|           ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER]), |           ?MESSAGES_DEAD_LETTERED_EXPIRED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_REJECTED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER | ||||||
|  |          ]), | ||||||
|     init([{queue_type, rabbit_quorum_queue}, {dead_letter_strategy, at_least_once}], |     init([{queue_type, rabbit_quorum_queue}, {dead_letter_strategy, at_least_once}], | ||||||
|          [?MESSAGES_DEAD_LETTERED_CONFIRMED_COUNTER, |          [?MESSAGES_DEAD_LETTERED_CONFIRMED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_EXPIRED_COUNTER, | ||||||
|  |           ?MESSAGES_DEAD_LETTERED_REJECTED_COUNTER, | ||||||
|           ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER |           ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT_COUNTER | ||||||
|          ]). |          ]). | ||||||
| 
 | 
 | ||||||
|  | @ -193,9 +186,9 @@ init(Labels = [{protocol, Protocol}], Extra) -> | ||||||
|     _ = seshat:new_group(?MODULE), |     _ = seshat:new_group(?MODULE), | ||||||
|     Counters = seshat:new(?MODULE, Labels, ?PROTOCOL_COUNTERS ++ Extra), |     Counters = seshat:new(?MODULE, Labels, ?PROTOCOL_COUNTERS ++ Extra), | ||||||
|     persistent_term:put({?MODULE, Protocol}, Counters); |     persistent_term:put({?MODULE, Protocol}, Counters); | ||||||
| init(Labels = [{queue_type, QueueType}, {dead_letter_strategy, DLS}], Extra) -> | init(Labels = [{queue_type, QueueType}, {dead_letter_strategy, DLS}], DeadLetterCounters) -> | ||||||
|     _ = seshat:new_group(?MODULE), |     _ = seshat:new_group(?MODULE), | ||||||
|     Counters = seshat:new(?MODULE, Labels, ?MESSAGES_DEAD_LETTERED_COUNTERS ++ Extra), |     Counters = seshat:new(?MODULE, Labels, DeadLetterCounters), | ||||||
|     persistent_term:put({?MODULE, QueueType, DLS}, Counters). |     persistent_term:put({?MODULE, QueueType, DLS}, Counters). | ||||||
| 
 | 
 | ||||||
| overview() -> | overview() -> | ||||||
|  | @ -263,9 +256,9 @@ consumer_deleted(Protocol) -> | ||||||
| 
 | 
 | ||||||
| messages_dead_lettered(Reason, QueueType, DeadLetterStrategy, Num) -> | messages_dead_lettered(Reason, QueueType, DeadLetterStrategy, Num) -> | ||||||
|     Index = case Reason of |     Index = case Reason of | ||||||
|  |                 maxlen -> ?MESSAGES_DEAD_LETTERED_MAXLEN; | ||||||
|                 expired -> ?MESSAGES_DEAD_LETTERED_EXPIRED; |                 expired -> ?MESSAGES_DEAD_LETTERED_EXPIRED; | ||||||
|                 rejected -> ?MESSAGES_DEAD_LETTERED_REJECTED; |                 rejected -> ?MESSAGES_DEAD_LETTERED_REJECTED; | ||||||
|                 maxlen -> ?MESSAGES_DEAD_LETTERED_MAXLEN; |  | ||||||
|                 delivery_limit -> ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT |                 delivery_limit -> ?MESSAGES_DEAD_LETTERED_DELIVERY_LIMIT | ||||||
|             end, |             end, | ||||||
|     counters:add(fetch(QueueType, DeadLetterStrategy), Index, Num). |     counters:add(fetch(QueueType, DeadLetterStrategy), Index, Num). | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| 
 | 
 | ||||||
| -include("rabbit_mqtt.hrl"). | -include("rabbit_mqtt.hrl"). | ||||||
| -include("rabbit_mqtt_packet.hrl"). | -include("rabbit_mqtt_packet.hrl"). | ||||||
|  | -include_lib("rabbit/include/rabbit_global_counters.hrl"). | ||||||
| -include_lib("stdlib/include/assert.hrl"). | -include_lib("stdlib/include/assert.hrl"). | ||||||
| 
 | 
 | ||||||
| -export([start/2, stop/1]). | -export([start/2, stop/1]). | ||||||
|  | @ -93,16 +94,18 @@ local_connection_pids() -> | ||||||
|     end. |     end. | ||||||
| 
 | 
 | ||||||
| init_global_counters() -> | init_global_counters() -> | ||||||
|     init_global_counters(?MQTT_PROTO_V3), |     lists:foreach(fun init_global_counters/1, [?MQTT_PROTO_V3, | ||||||
|     init_global_counters(?MQTT_PROTO_V4), |                                                ?MQTT_PROTO_V4, | ||||||
|     init_global_counters(?MQTT_PROTO_V5). |                                                ?MQTT_PROTO_V5]). | ||||||
| 
 | 
 | ||||||
| init_global_counters(ProtoVer) -> | init_global_counters(ProtoVer) -> | ||||||
|     Proto = {protocol, ProtoVer}, |     Proto = {protocol, ProtoVer}, | ||||||
|     rabbit_global_counters:init([Proto]), |     rabbit_global_counters:init([Proto]), | ||||||
|     rabbit_global_counters:init([Proto, {queue_type, ?QUEUE_TYPE_QOS_0}]), |  | ||||||
|     rabbit_global_counters:init([Proto, {queue_type, rabbit_classic_queue}]), |     rabbit_global_counters:init([Proto, {queue_type, rabbit_classic_queue}]), | ||||||
|     rabbit_global_counters:init([Proto, {queue_type, rabbit_quorum_queue}]). |     rabbit_global_counters:init([Proto, {queue_type, rabbit_quorum_queue}]), | ||||||
|  |     rabbit_global_counters:init([Proto, {queue_type, ?QUEUE_TYPE_QOS_0}]), | ||||||
|  |     rabbit_global_counters:init([{queue_type, ?QUEUE_TYPE_QOS_0}, {dead_letter_strategy, disabled}], | ||||||
|  |                                 [?MESSAGES_DEAD_LETTERED_MAXLEN_COUNTER]). | ||||||
| 
 | 
 | ||||||
| persist_static_configuration() -> | persist_static_configuration() -> | ||||||
|     rabbit_mqtt_util:init_sparkplug(), |     rabbit_mqtt_util:init_sparkplug(), | ||||||
|  |  | ||||||
|  | @ -1982,6 +1982,8 @@ handle_queue_event({queue_event, ?QUEUE_TYPE_QOS_0, Msg}, | ||||||
|                 false -> |                 false -> | ||||||
|                     deliver_one_to_client(Msg, false, State0); |                     deliver_one_to_client(Msg, false, State0); | ||||||
|                 true -> |                 true -> | ||||||
|  |                     rabbit_global_counters:messages_dead_lettered( | ||||||
|  |                       maxlen, ?QUEUE_TYPE_QOS_0, disabled, 1), | ||||||
|                     State0#state{qos0_messages_dropped = N + 1} |                     State0#state{qos0_messages_dropped = N + 1} | ||||||
|             end, |             end, | ||||||
|     {ok, State}; |     {ok, State}; | ||||||
|  |  | ||||||
|  | @ -254,7 +254,22 @@ event_authentication_failure(Config) -> | ||||||
| %% Test that queue type rabbit_mqtt_qos0_queue drops QoS 0 messages when its | %% Test that queue type rabbit_mqtt_qos0_queue drops QoS 0 messages when its | ||||||
| %% max length is reached. | %% max length is reached. | ||||||
| rabbit_mqtt_qos0_queue_overflow(Config) -> | rabbit_mqtt_qos0_queue_overflow(Config) -> | ||||||
|     ok = rabbit_ct_broker_helpers:enable_feature_flag(Config, rabbit_mqtt_qos0_queue), |     ProtoVer = case ?config(mqtt_version, Config) of | ||||||
|  |                    v4 -> mqtt311; | ||||||
|  |                    v5 -> mqtt50 | ||||||
|  |                end, | ||||||
|  |     QType = rabbit_mqtt_qos0_queue, | ||||||
|  | 
 | ||||||
|  |     #{ | ||||||
|  |       [{protocol, ProtoVer}, {queue_type, QType}] := | ||||||
|  |       #{messages_delivered_total := 0, | ||||||
|  |         messages_delivered_consume_auto_ack_total := 0}, | ||||||
|  | 
 | ||||||
|  |       [{queue_type, QType}, {dead_letter_strategy, disabled}] := | ||||||
|  |       #{messages_dead_lettered_maxlen_total := NumDeadLettered} | ||||||
|  |      } = rabbit_ct_broker_helpers:rpc(Config, rabbit_global_counters, overview, []), | ||||||
|  | 
 | ||||||
|  |     ok = rabbit_ct_broker_helpers:enable_feature_flag(Config, QType), | ||||||
| 
 | 
 | ||||||
|     Topic = atom_to_binary(?FUNCTION_NAME), |     Topic = atom_to_binary(?FUNCTION_NAME), | ||||||
|     Msg = binary:copy(<<"x">>, 4000), |     Msg = binary:copy(<<"x">>, 4000), | ||||||
|  | @ -289,7 +304,8 @@ rabbit_mqtt_qos0_queue_overflow(Config) -> | ||||||
|     {status, _, _, [_, _, _, _, Misc]} = sys:get_status(ServerConnectionPid), |     {status, _, _, [_, _, _, _, Misc]} = sys:get_status(ServerConnectionPid), | ||||||
|     [State] = [S || {data, [{"State", S}]} <- Misc], |     [State] = [S || {data, [{"State", S}]} <- Misc], | ||||||
|     #{proc_state := #{qos0_messages_dropped := NumDropped}} = State, |     #{proc_state := #{qos0_messages_dropped := NumDropped}} = State, | ||||||
|     ct:pal("NumReceived=~b~nNumDropped=~b", [NumReceived, NumDropped]), | 
 | ||||||
|  |     ct:pal("NumReceived=~b NumDropped=~b", [NumReceived, NumDropped]), | ||||||
| 
 | 
 | ||||||
|     %% We expect that |     %% We expect that | ||||||
|     %% 1. all sent messages were either received or dropped |     %% 1. all sent messages were either received or dropped | ||||||
|  | @ -302,6 +318,19 @@ rabbit_mqtt_qos0_queue_overflow(Config) -> | ||||||
|     %% of mailbox_soft_limit=200 should not be dropped |     %% of mailbox_soft_limit=200 should not be dropped | ||||||
|     ?assert(NumReceived >= 200), |     ?assert(NumReceived >= 200), | ||||||
| 
 | 
 | ||||||
|  |     %% Assert that Prometheus metrics counted correctly. | ||||||
|  |     ExpectedNumDeadLettered = NumDeadLettered + NumDropped, | ||||||
|  |     ?assertMatch( | ||||||
|  |        #{ | ||||||
|  |          [{protocol, ProtoVer}, {queue_type, QType}] := | ||||||
|  |          #{messages_delivered_total := NumReceived, | ||||||
|  |            messages_delivered_consume_auto_ack_total := NumReceived}, | ||||||
|  | 
 | ||||||
|  |          [{queue_type, QType}, {dead_letter_strategy, disabled}] := | ||||||
|  |          #{messages_dead_lettered_maxlen_total := ExpectedNumDeadLettered} | ||||||
|  |         }, | ||||||
|  |        rabbit_ct_broker_helpers:rpc(Config, rabbit_global_counters, overview, [])), | ||||||
|  | 
 | ||||||
|     ok = emqtt:disconnect(Sub), |     ok = emqtt:disconnect(Sub), | ||||||
|     ok = emqtt:disconnect(Pub). |     ok = emqtt:disconnect(Pub). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -866,7 +866,7 @@ at_most_once_dead_letter_detect_cycle(Config) -> | ||||||
|     ok = emqtt:disconnect(Pub), |     ok = emqtt:disconnect(Pub), | ||||||
|     %% Given our subscribing client is disconnected, the message should be dead lettered after 1 ms. |     %% Given our subscribing client is disconnected, the message should be dead lettered after 1 ms. | ||||||
|     %% However, due to the dead letter cycle, we expect the message to be dropped. |     %% However, due to the dead letter cycle, we expect the message to be dropped. | ||||||
|     timer:sleep(5), |     timer:sleep(20), | ||||||
|     Sub2 = connect(SubClientId, Config, [{clean_start, false}]), |     Sub2 = connect(SubClientId, Config, [{clean_start, false}]), | ||||||
|     assert_nothing_received(), |     assert_nothing_received(), | ||||||
|     %% Double check that the message was indeed (exactly once) dead lettered. |     %% Double check that the message was indeed (exactly once) dead lettered. | ||||||
|  |  | ||||||
|  | @ -80,8 +80,9 @@ To generate these: | ||||||
| Metrics `rabbitmq_global_messages_dead_lettered_*` have labels `queue_type` and `dead_letter_strategy`. | Metrics `rabbitmq_global_messages_dead_lettered_*` have labels `queue_type` and `dead_letter_strategy`. | ||||||
| 
 | 
 | ||||||
| Label `queue_type` denotes the type of queue messages were discarded from. It can have value | Label `queue_type` denotes the type of queue messages were discarded from. It can have value | ||||||
| * `rabbit_classic_queue`, or | * `rabbit_classic_queue`, | ||||||
| * `rabbit_quorum_queue` | * `rabbit_quorum_queue`, or | ||||||
|  | * `rabbit_mqtt_qos0_queue` | ||||||
| 
 | 
 | ||||||
| (Queue type `rabbit_stream_queue` does not dead letter messages.) | (Queue type `rabbit_stream_queue` does not dead letter messages.) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue