Skip to content

Commit 028b692

Browse files
committed
Add a health check for testing the node connection limit
1 parent 4f9a55b commit 028b692

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

deps/rabbitmq_management/priv/www/api/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,18 @@ <h2>Reference</h2>
12521252
Service Unavailable.
12531253
</td>
12541254
</tr>
1255+
<tr>
1256+
<td>X</td>
1257+
<td></td>
1258+
<td></td>
1259+
<td></td>
1260+
<td class="path">/api/health/checks/below-node-connection-limit</td>
1261+
<td>
1262+
Responds a 200 OK if the target node has fewer connections to the AMQP
1263+
and AMQPS ports than the configured maximum, otherwise responds with a
1264+
503 Service Unavailable.
1265+
</td>
1266+
</tr>
12551267
<tr>
12561268
<td>X</td>
12571269
<td></td>

deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ dispatcher() ->
208208
{"/health/checks/quorum-queues-without-elected-leaders/vhost/:vhost/pattern/:pattern", rabbit_mgmt_wm_health_check_quorum_queues_without_elected_leaders, []},
209209
{"/health/checks/node-is-quorum-critical", rabbit_mgmt_wm_health_check_node_is_quorum_critical, []},
210210
{"/health/checks/is-in-service", rabbit_mgmt_wm_health_check_is_in_service, []},
211+
{"/health/checks/below-node-connection-limit", rabbit_mgmt_wm_health_check_below_node_connection_limit, []},
211212
{"/reset", rabbit_mgmt_wm_reset, []},
212213
{"/reset/:node", rabbit_mgmt_wm_reset, []},
213214
{"/rebalance/queues", rabbit_mgmt_wm_rebalance_queues, [{queues, all}]},
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
8+
-module(rabbit_mgmt_wm_health_check_below_node_connection_limit).
9+
10+
-export([init/2]).
11+
-export([to_json/2, content_types_provided/2]).
12+
-export([variances/2]).
13+
14+
-include("rabbit_mgmt.hrl").
15+
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
16+
17+
init(Req, _State) ->
18+
Req1 = rabbit_mgmt_headers:set_no_cache_headers(
19+
rabbit_mgmt_headers:set_common_permission_headers(
20+
Req, ?MODULE), ?MODULE),
21+
{cowboy_rest, Req1, #context{}}.
22+
23+
variances(Req, Context) ->
24+
{[<<"accept-encoding">>, <<"origin">>], Req, Context}.
25+
26+
content_types_provided(ReqData, Context) ->
27+
{rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
28+
29+
to_json(ReqData, Context) ->
30+
ActiveConns = lists:foldl(
31+
fun(Protocol, Acc) ->
32+
Acc + protocol_connection_count(Protocol)
33+
end, 0, [amqp, 'amqp/ssl']),
34+
Limit = rabbit_misc:get_env(rabbit, connection_max, infinity),
35+
case ActiveConns < Limit of
36+
true ->
37+
rabbit_mgmt_util:reply(
38+
#{status => ok,
39+
limit => Limit,
40+
connections => ActiveConns}, ReqData, Context);
41+
false ->
42+
Body = #{
43+
status => failed,
44+
reason => <<"node connection limit is reached">>,
45+
limit => Limit,
46+
connections => ActiveConns
47+
},
48+
{Response, ReqData1, Context1} = rabbit_mgmt_util:reply(
49+
Body, ReqData, Context),
50+
{stop,
51+
cowboy_req:reply(
52+
?HEALTH_CHECK_FAILURE_STATUS, #{}, Response, ReqData1),
53+
Context1}
54+
end.
55+
56+
protocol_connection_count(Protocol) ->
57+
case rabbit_networking:ranch_ref_of_protocol(Protocol) of
58+
undefined ->
59+
0;
60+
RanchRef ->
61+
#{active_connections := Count} = ranch:info(RanchRef),
62+
Count
63+
end.

deps/rabbitmq_management/test/rabbit_mgmt_http_health_checks_SUITE.erl

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ all_tests() -> [
5151
protocol_listener_test,
5252
port_listener_test,
5353
certificate_expiration_test,
54-
is_in_service_test
54+
is_in_service_test,
55+
below_node_connection_limit_test
5556
].
5657

5758
%% -------------------------------------------------------------------
@@ -470,8 +471,36 @@ is_in_service_test(Config) ->
470471

471472
passed.
472473

474+
below_node_connection_limit_test(Config) ->
475+
Path = "/health/checks/below-node-connection-limit",
476+
Check0 = http_get(Config, Path, ?OK),
477+
?assertEqual(<<"ok">>, maps:get(status, Check0)),
478+
?assertEqual(0, maps:get(connections, Check0)),
479+
?assertEqual(<<"infinity">>, maps:get(limit, Check0)),
480+
481+
%% Set the connection limit low and open 'limit' connections.
482+
Limit = 10,
483+
rabbit_ct_broker_helpers:rpc(
484+
Config, 0, application, set_env, [rabbit, connection_max, Limit]),
485+
Connections = [rabbit_ct_client_helpers:open_unmanaged_connection(Config, 0) || _ <- lists:seq(1, Limit)],
486+
true = lists:all(fun(E) -> is_pid(E) end, Connections),
487+
{error, not_allowed} = rabbit_ct_client_helpers:open_unmanaged_connection(Config, 0),
488+
489+
Body0 = http_get_failed(Config, Path),
490+
?assertEqual(<<"failed">>, maps:get(<<"status">>, Body0)),
491+
?assertEqual(10, maps:get(<<"limit">>, Body0)),
492+
?assertEqual(10, maps:get(<<"connections">>, Body0)),
493+
494+
%% Clean up the connections and reset the limit.
495+
[catch rabbit_ct_client_helpers:close_connection(C) || C <- Connections],
496+
rabbit_ct_broker_helpers:rpc(
497+
Config, 0, application, set_env, [rabbit, connection_max, infinity]),
498+
499+
passed.
500+
473501
http_get_failed(Config, Path) ->
474502
{ok, {{_, Code, _}, _, ResBody}} = req(Config, get, Path, [auth_header("guest", "guest")]),
503+
ct:pal("GET ~s: ~w ~w", [Path, Code, ResBody]),
475504
?assertEqual(Code, ?HEALTH_CHECK_FAILURE_STATUS),
476505
rabbit_json:decode(rabbit_data_coercion:to_binary(ResBody)).
477506

0 commit comments

Comments
 (0)