Merge pull request #14006 from rabbitmq/delete-qos0-queue
Delete mqtt qos0 queue when mqtt 5.0 connection is closed
This commit is contained in:
commit
d8b3288857
|
@ -1906,7 +1906,7 @@ log_delayed_will_failure(Topic, ClientId, Reason) ->
|
||||||
[Topic, ClientId, Reason]).
|
[Topic, ClientId, Reason]).
|
||||||
|
|
||||||
maybe_delete_mqtt_qos0_queue(
|
maybe_delete_mqtt_qos0_queue(
|
||||||
State = #state{cfg = #cfg{clean_start = true},
|
State = #state{cfg = #cfg{session_expiry_interval_secs = 0},
|
||||||
auth_state = #auth_state{user = #user{username = Username}}}) ->
|
auth_state = #auth_state{user = #user{username = Username}}}) ->
|
||||||
case get_queue(?QOS_0, State) of
|
case get_queue(?QOS_0, State) of
|
||||||
{ok, Q} ->
|
{ok, Q} ->
|
||||||
|
|
|
@ -211,6 +211,7 @@ init_per_testcase(T, Config)
|
||||||
init_per_testcase0(T, Config);
|
init_per_testcase0(T, Config);
|
||||||
init_per_testcase(T, Config)
|
init_per_testcase(T, Config)
|
||||||
when T =:= clean_session_disconnect_client;
|
when T =:= clean_session_disconnect_client;
|
||||||
|
T =:= zero_session_expiry_interval_disconnect_client;
|
||||||
T =:= clean_session_node_restart;
|
T =:= clean_session_node_restart;
|
||||||
T =:= clean_session_node_kill;
|
T =:= clean_session_node_kill;
|
||||||
T =:= notify_consumer_qos0_queue_deleted ->
|
T =:= notify_consumer_qos0_queue_deleted ->
|
||||||
|
@ -229,6 +230,7 @@ end_per_testcase(T, Config)
|
||||||
end_per_testcase0(T, Config);
|
end_per_testcase0(T, Config);
|
||||||
end_per_testcase(T, Config)
|
end_per_testcase(T, Config)
|
||||||
when T =:= clean_session_disconnect_client;
|
when T =:= clean_session_disconnect_client;
|
||||||
|
T =:= zero_session_expiry_interval_disconnect_client;
|
||||||
T =:= clean_session_node_restart;
|
T =:= clean_session_node_restart;
|
||||||
T =:= clean_session_node_kill;
|
T =:= clean_session_node_kill;
|
||||||
T =:= notify_consumer_qos0_queue_deleted ->
|
T =:= notify_consumer_qos0_queue_deleted ->
|
||||||
|
|
|
@ -71,6 +71,7 @@ cluster_size_1_tests() ->
|
||||||
session_expiry_reconnect_non_zero,
|
session_expiry_reconnect_non_zero,
|
||||||
session_expiry_reconnect_zero,
|
session_expiry_reconnect_zero,
|
||||||
session_expiry_reconnect_infinity_to_zero,
|
session_expiry_reconnect_infinity_to_zero,
|
||||||
|
zero_session_expiry_disconnect_autodeletes_qos0_queue,
|
||||||
client_publish_qos2,
|
client_publish_qos2,
|
||||||
client_rejects_publish,
|
client_rejects_publish,
|
||||||
client_receive_maximum_min,
|
client_receive_maximum_min,
|
||||||
|
@ -188,6 +189,12 @@ init_per_testcase(T, Config)
|
||||||
ok = rpc(Config, application, set_env, [?APP, Par, infinity]),
|
ok = rpc(Config, application, set_env, [?APP, Par, infinity]),
|
||||||
Config1 = rabbit_ct_helpers:set_config(Config, {Par, Default}),
|
Config1 = rabbit_ct_helpers:set_config(Config, {Par, Default}),
|
||||||
init_per_testcase0(T, Config1);
|
init_per_testcase0(T, Config1);
|
||||||
|
|
||||||
|
init_per_testcase(T, Config)
|
||||||
|
when T =:= zero_session_expiry_disconnect_autodeletes_qos0_queue ->
|
||||||
|
rpc(Config, rabbit_registry, register, [queue, <<"qos0">>, rabbit_mqtt_qos0_queue]),
|
||||||
|
init_per_testcase0(T, Config);
|
||||||
|
|
||||||
init_per_testcase(T, Config) ->
|
init_per_testcase(T, Config) ->
|
||||||
init_per_testcase0(T, Config).
|
init_per_testcase0(T, Config).
|
||||||
|
|
||||||
|
@ -202,6 +209,11 @@ end_per_testcase(T, Config)
|
||||||
Default = ?config(Par, Config),
|
Default = ?config(Par, Config),
|
||||||
ok = rpc(Config, application, set_env, [?APP, Par, Default]),
|
ok = rpc(Config, application, set_env, [?APP, Par, Default]),
|
||||||
end_per_testcase0(T, Config);
|
end_per_testcase0(T, Config);
|
||||||
|
end_per_testcase(T, Config)
|
||||||
|
when T =:= zero_session_expiry_disconnect_autodeletes_qos0_queue ->
|
||||||
|
ok = rpc(Config, rabbit_registry, unregister, [queue, <<"qos0">>]),
|
||||||
|
init_per_testcase0(T, Config);
|
||||||
|
|
||||||
end_per_testcase(T, Config) ->
|
end_per_testcase(T, Config) ->
|
||||||
end_per_testcase0(T, Config).
|
end_per_testcase0(T, Config).
|
||||||
|
|
||||||
|
@ -389,6 +401,22 @@ session_expiry_quorum_queue_disconnect_decrease(Config) ->
|
||||||
ok = session_expiry_disconnect_decrease(rabbit_quorum_queue, Config),
|
ok = session_expiry_disconnect_decrease(rabbit_quorum_queue, Config),
|
||||||
ok = rpc(Config, application, unset_env, [?APP, durable_queue_type]).
|
ok = rpc(Config, application, unset_env, [?APP, durable_queue_type]).
|
||||||
|
|
||||||
|
zero_session_expiry_disconnect_autodeletes_qos0_queue(Config) ->
|
||||||
|
ClientId = ?FUNCTION_NAME,
|
||||||
|
C = connect(ClientId, Config, [
|
||||||
|
{clean_start, false},
|
||||||
|
{properties, #{'Session-Expiry-Interval' => 0}}]),
|
||||||
|
{ok, _, _} = emqtt:subscribe(C, <<"topic0">>, qos0),
|
||||||
|
QsQos0 = rpc(Config, rabbit_amqqueue, list_by_type, [rabbit_mqtt_qos0_queue]),
|
||||||
|
?assertEqual(1, length(QsQos0)),
|
||||||
|
|
||||||
|
ok = emqtt:disconnect(C),
|
||||||
|
%% After terminating a clean session, we expect any session state to be cleaned up on the server.
|
||||||
|
%% Give the node some time to clean up the MQTT QoS 0 queue.
|
||||||
|
timer:sleep(200),
|
||||||
|
L = rpc(Config, rabbit_amqqueue, list, []),
|
||||||
|
?assertEqual(0, length(L)).
|
||||||
|
|
||||||
session_expiry_disconnect_decrease(QueueType, Config) ->
|
session_expiry_disconnect_decrease(QueueType, Config) ->
|
||||||
ClientId = ?FUNCTION_NAME,
|
ClientId = ?FUNCTION_NAME,
|
||||||
C1 = connect(ClientId, Config, [{properties, #{'Session-Expiry-Interval' => 100}}]),
|
C1 = connect(ClientId, Config, [{properties, #{'Session-Expiry-Interval' => 100}}]),
|
||||||
|
|
|
@ -103,3 +103,4 @@ maintenance(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
||||||
notify_consumer_classic_queue_deleted(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
notify_consumer_classic_queue_deleted(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
||||||
notify_consumer_quorum_queue_deleted(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
notify_consumer_quorum_queue_deleted(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
||||||
notify_consumer_qos0_queue_deleted(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
notify_consumer_qos0_queue_deleted(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
||||||
|
zero_session_expiry_interval_disconnect_client(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config).
|
|
@ -12,7 +12,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chromedriver": "^135.0",
|
"chromedriver": "^137.0",
|
||||||
"ejs": "^3.1.8",
|
"ejs": "^3.1.8",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"geckodriver": "^3.0.2",
|
"geckodriver": "^3.0.2",
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
auth_backends.1 = rabbit_auth_backend_internal
|
auth_backends.1 = rabbit_auth_backend_internal
|
||||||
|
|
||||||
management.login_session_timeout = 1
|
|
||||||
load_definitions = ${IMPORT_DIR}/users.json
|
load_definitions = ${IMPORT_DIR}/users.json
|
||||||
|
|
||||||
|
management.login_session_timeout = 1
|
||||||
|
|
||||||
loopback_users = none
|
loopback_users = none
|
||||||
|
|
||||||
|
log.console.level = debug
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
const { By, Key, until, Builder } = require('selenium-webdriver')
|
||||||
|
require('chromedriver')
|
||||||
|
const assert = require('assert')
|
||||||
|
const { buildDriver, goToHome, goToQueue, captureScreensFor, teardown, doUntil, findTableRow } = require('../utils')
|
||||||
|
const { createQueue, getManagementUrl, basicAuthorization } = require('../mgt-api')
|
||||||
|
const { openConnection, getConnectionOptions } = require('../mqtt')
|
||||||
|
|
||||||
|
const LoginPage = require('../pageobjects/LoginPage')
|
||||||
|
const OverviewPage = require('../pageobjects/OverviewPage')
|
||||||
|
const QueuesAndStreamsPage = require('../pageobjects/QueuesAndStreamsPage')
|
||||||
|
const QueuePage = require('../pageobjects/QueuePage')
|
||||||
|
const ConnectionsPage = require('../pageobjects/ConnectionsPage');
|
||||||
|
|
||||||
|
|
||||||
|
describe('Given an MQTT 5.0 connection with a qos 0 subscription with zero sessionExpiryInterval', function () {
|
||||||
|
let login
|
||||||
|
let queuesAndStreamsPage
|
||||||
|
let queuePage
|
||||||
|
let overview
|
||||||
|
let captureScreen
|
||||||
|
let queueName
|
||||||
|
|
||||||
|
let mqttClient
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
driver = buildDriver()
|
||||||
|
await goToHome(driver)
|
||||||
|
login = new LoginPage(driver)
|
||||||
|
overview = new OverviewPage(driver)
|
||||||
|
queuePage = new QueuePage(driver)
|
||||||
|
connectionsPage = new ConnectionsPage(driver)
|
||||||
|
queuesAndStreamsPage = new QueuesAndStreamsPage(driver)
|
||||||
|
captureScreen = captureScreensFor(driver, __filename)
|
||||||
|
|
||||||
|
await login.login('management', 'guest')
|
||||||
|
if (!await overview.isLoaded()) {
|
||||||
|
throw new Error('Failed to login')
|
||||||
|
}
|
||||||
|
//await overview.selectRefreshOption("Do not refresh")
|
||||||
|
|
||||||
|
queueName = "test_" + Math.floor(Math.random() * 1000)
|
||||||
|
createQueue(getManagementUrl(), basicAuthorization("management", "guest"),
|
||||||
|
"/", queueName, {
|
||||||
|
"x-queue-type": "quorum"
|
||||||
|
})
|
||||||
|
|
||||||
|
mqttClient = openConnection(getConnectionOptions())
|
||||||
|
let subscribed = new Promise((resolve, reject) => {
|
||||||
|
mqttClient.on('error', function(err) {
|
||||||
|
reject(err)
|
||||||
|
assert.fail("Mqtt connection failed due to " + err)
|
||||||
|
}),
|
||||||
|
mqttClient.on('connect', function(err) {
|
||||||
|
mqttClient.subscribe(queueName, {qos:0}, function (err2) {
|
||||||
|
if (!err2) {
|
||||||
|
resolve("ok")
|
||||||
|
}else {
|
||||||
|
reject(err2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
assert.equal("ok", await subscribed)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can view mqtt qos0 queue', async function () {
|
||||||
|
await overview.clickOnQueuesTab()
|
||||||
|
|
||||||
|
let table = await doUntil(function() {
|
||||||
|
return queuesAndStreamsPage.getQueuesTable()
|
||||||
|
}, function(t) {
|
||||||
|
return findTableRow(t, function(row) {
|
||||||
|
return row[2] === 'rabbit_mqtt_qos0_queue'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let mqttQueueName = findTableRow(table, function(row) {
|
||||||
|
return row[2] === 'rabbit_mqtt_qos0_queue'
|
||||||
|
})[1]
|
||||||
|
|
||||||
|
await goToQueue(driver, "/", mqttQueueName)
|
||||||
|
await queuePage.isLoaded()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('when the connection is closed, the mqtt qos0 queue should be removed', async function () {
|
||||||
|
|
||||||
|
mqttClient.end()
|
||||||
|
|
||||||
|
await overview.clickOnConnectionsTab()
|
||||||
|
await doUntil(async function() {
|
||||||
|
return connectionsPage.getPagingSectionHeaderText()
|
||||||
|
}, function(header) {
|
||||||
|
return header === "All connections (0)"
|
||||||
|
}, 6000)
|
||||||
|
|
||||||
|
await overview.clickOnQueuesTab()
|
||||||
|
await doUntil(function() {
|
||||||
|
return queuesAndStreamsPage.getQueuesTable()
|
||||||
|
}, function(table) {
|
||||||
|
return !findTableRow(table, function(row) {
|
||||||
|
return row[2] === 'rabbit_mqtt_qos0_queue'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
after(async function () {
|
||||||
|
await teardown(driver, this, captureScreen)
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue