Skip to content

Commit 2519671

Browse files
Tomasz Stepniakdahlerlend
authored andcommitted
WL#15440 MySQL REST Service (MRS) - Umbrella WL
Bug#37526847 Changing [mysql_rest_service].router_id in the config file has no effect. When Router detects that there is the same Router registered but with different router_id it fails to start. Change-Id: Ib2e9c1794e7da18e8efb0d4fe057e76b54bf75a7
1 parent 9835a85 commit 2519671

File tree

7 files changed

+446
-19
lines changed

7 files changed

+446
-19
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#ifndef ROUTER_SRC_REST_MRS_SRC_MRS_DATABASE_QUERY_ROUTER_INFO_H_
26+
#define ROUTER_SRC_REST_MRS_SRC_MRS_DATABASE_QUERY_ROUTER_INFO_H_
27+
28+
#include "mrs/database/helper/query.h"
29+
30+
#include <string>
31+
32+
namespace mrs {
33+
namespace database {
34+
35+
class QueryRouterInfo : private Query {
36+
public:
37+
std::optional<uint64_t> find_existing_router_instances(
38+
MySQLSession *session, const std::string &router_name,
39+
const std::string &address);
40+
41+
private:
42+
void on_row(const ResultRow &r) override;
43+
44+
std::optional<uint64_t> id_;
45+
};
46+
47+
} // namespace database
48+
} // namespace mrs
49+
50+
#endif // ROUTER_SRC_REST_MRS_SRC_MRS_DATABASE_QUERY_ROUTER_INFO_H_

router/src/mysql_rest_service/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ ADD_STATIC_LIBRARY(mysql_rest_service_db
106106
mrs/database/query_rest_task.cc
107107
mrs/database/query_rest_task_status.cc
108108
mrs/database/query_rest_table_updater.cc
109+
mrs/database/query_router_info.cc
109110
mrs/database/query_state.cc
110111
mrs/database/query_statistics.cc
111112
mrs/database/query_version.cc
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#include <stdexcept>
26+
27+
#include "mrs/database/query_router_info.h"
28+
29+
#include "mysqld_error.h"
30+
31+
namespace mrs {
32+
namespace database {
33+
34+
std::optional<uint64_t> QueryRouterInfo::find_existing_router_instances(
35+
MySQLSession *session, const std::string &router_name,
36+
const std::string &address) {
37+
try {
38+
mysqlrouter::sqlstring sql_query{
39+
"SELECT `id` FROM mysql_rest_service_metadata.router WHERE"
40+
" router_name = ? AND address = ?"};
41+
sql_query << router_name << address;
42+
query(session, sql_query);
43+
} catch (const mysqlrouter::MySQLSession::Error &error) {
44+
if (error.code() != ER_NO_SUCH_TABLE &&
45+
error.code() != ER_TABLEACCESS_DENIED_ERROR) {
46+
throw;
47+
}
48+
}
49+
return id_;
50+
}
51+
52+
void QueryRouterInfo::on_row(const ResultRow &r) {
53+
if (r.size() != 1) {
54+
throw std::runtime_error(
55+
"Could not fetch router information from "
56+
"`mysql_rest_service_metadata`.`router`");
57+
}
58+
59+
try {
60+
id_ = std::stoi(r[0]);
61+
} catch (const std::exception &) {
62+
id_ = std::nullopt;
63+
}
64+
}
65+
66+
} // namespace database
67+
} // namespace mrs

router/src/mysql_rest_service/src/mysql_rest_service_plugin.cc

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "my_sys.h"
3737
#include "my_thread.h" // NOLINT(build/include_subdir)
3838
#include "mysqld_error.h" // NOLINT(build/include_subdir)
39+
#include "socket_operations.h"
3940

4041
#include "keyring/keyring_manager.h"
4142
#include "mysql/harness/loader.h"
@@ -49,6 +50,7 @@
4950
#include "helper/task_control.h"
5051
#include "mrs/authentication/auth_handler_factory.h"
5152
#include "mrs/database/query_factory_proxy.h"
53+
#include "mrs/database/query_router_info.h"
5254
#include "mrs/database/schema_monitor.h"
5355
#include "mrs/database/slow_query_monitor.h"
5456
#include "mrs/endpoint/handler/handler_debug.cc"
@@ -85,6 +87,13 @@ void trace_error(const char *variable_user, const char *access,
8587
variable_user, e.code(), e.message().c_str());
8688
}
8789

90+
std::optional<uint64_t> find_existing_routers(
91+
mysqlrouter::MySQLSession *session, const std::string &router_name,
92+
const std::string &address) {
93+
mrs::database::QueryRouterInfo q;
94+
return q.find_existing_router_instances(session, router_name, address);
95+
}
96+
8897
} // namespace
8998

9099
class MrsModule {
@@ -112,8 +121,10 @@ class MrsModule {
112121
// TODO(areliga): remove that when rebased to the Router version that fixes
113122
// ODR issues
114123
my_init();
124+
collector::MysqlCacheManager::CachedObject conn1;
125+
115126
try {
116-
auto conn1 = mysql_connection_cache.get_instance(
127+
conn1 = mysql_connection_cache.get_instance(
117128
collector::kMySQLConnectionMetadataRO, true);
118129

119130
check_version_compatibility(conn1.get());
@@ -150,6 +161,18 @@ class MrsModule {
150161
"failed. For more informations look at previous error messages.");
151162
}
152163

164+
auto socket_ops = mysql_harness::SocketOperations::instance();
165+
const auto name = configuration.router_name_;
166+
const auto address = socket_ops->get_local_hostname();
167+
const auto existing_id_maybe =
168+
find_existing_routers(conn1.get(), name, address);
169+
if (existing_id_maybe && *existing_id_maybe != configuration.router_id_) {
170+
throw std::runtime_error(
171+
"Metadata already contains Router registered as '" + name + "' at '" +
172+
address + "' with id: " + std::to_string(*existing_id_maybe) +
173+
", new id: " + std::to_string(configuration.router_id_));
174+
}
175+
153176
return true;
154177
}
155178

router/tests/component/data/local_modules/common_statements.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ var defaults = {
130130
router_hostname: "router-host",
131131
router_options: "",
132132
router_name: "test_router",
133+
mrs_router_id: 1,
134+
mrs_basedir: "",
133135
};
134136

135137
function ensure_type(options, field, expected_type) {
@@ -1433,6 +1435,47 @@ function get_response(stmt_key, options) {
14331435
rows: [[options.routing_guidelines]]
14341436
}
14351437
};
1438+
case "mrs_set_sql_mode":
1439+
return {"stmt": "SET @@SESSION.sql_mode=DEFAULT;", "ok": {}};
1440+
case "mrs_set_meta_provider_role":
1441+
return {"stmt": "SET ROLE mysql_rest_service_meta_provider", "ok": {}};
1442+
case "mrs_select_version":
1443+
return {
1444+
stmt:
1445+
"SELECT substring_index(@@version, '.', 1), concat(@@version_comment, @@version)",
1446+
result: {
1447+
columns: [
1448+
{
1449+
"name": "substring_index(@@version, '.', 1)",
1450+
"type": "VAR_STRING"
1451+
},
1452+
{
1453+
"name": "concat(@@version_comment, @@version)",
1454+
"type": "VAR_STRING"
1455+
}
1456+
],
1457+
rows: [["9", "Source distribution mrs"]],
1458+
}
1459+
};
1460+
case "mrs_select_basedir":
1461+
return {
1462+
stmt: "SELECT @@basedir",
1463+
result: {
1464+
columns: [{"name": "@@basedir", "type": "VAR_STRING"}],
1465+
rows: [[options.mrs_basedir]],
1466+
}
1467+
};
1468+
case "mrs_set_data_provider_role":
1469+
return {"stmt": "SET ROLE mysql_rest_service_data_provider", "ok": {}};
1470+
case "mrs_select_router_id":
1471+
return {
1472+
stmt_regex:
1473+
"SELECT `id` FROM mysql_rest_service_metadata.router WHERE router_name = '.*' AND address = '.*'",
1474+
result: {
1475+
columns: [{"name": "id", "type": "LONG"}],
1476+
rows: [[options.mrs_router_id]],
1477+
}
1478+
};
14361479
};
14371480
};
14381481

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
var common_stmts = require("common_statements");
2+
var gr_memberships = require("gr_memberships");
3+
4+
if (mysqld.global.gr_node_host === undefined) {
5+
mysqld.global.gr_node_host = "127.0.0.1";
6+
}
7+
8+
if (mysqld.global.gr_id === undefined) {
9+
mysqld.global.gr_id = "uuid";
10+
}
11+
12+
if (mysqld.global.gr_nodes === undefined) {
13+
mysqld.global.gr_nodes = [];
14+
}
15+
16+
if (mysqld.global.cluster_nodes === undefined) {
17+
mysqld.global.cluster_nodes = [];
18+
}
19+
20+
if (mysqld.global.notices === undefined) {
21+
mysqld.global.notices = [];
22+
}
23+
24+
if (mysqld.global.md_query_count === undefined) {
25+
mysqld.global.md_query_count = 0;
26+
}
27+
28+
if (mysqld.global.transaction_count === undefined) {
29+
mysqld.global.transaction_count = 0;
30+
}
31+
32+
if (mysqld.global.cluster_name === undefined) {
33+
mysqld.global.cluster_name = "test";
34+
}
35+
36+
if (mysqld.global.mrs_router_id === undefined) {
37+
mysqld.global.mrs_router_id = 1;
38+
}
39+
40+
({
41+
handshake: {
42+
auth: {
43+
username: mysqld.global.user,
44+
password: mysqld.global.password,
45+
},
46+
greeting: {server_version: mysqld.global.server_version}
47+
},
48+
stmts: function(stmt) {
49+
// ensure the cluster-type is set even if set_mock_metadata() did not set
50+
// it.
51+
if (mysqld.global.cluster_type === undefined) {
52+
mysqld.global.cluster_type = "gr";
53+
}
54+
55+
var members = gr_memberships.gr_members(
56+
mysqld.global.gr_node_host, mysqld.global.gr_nodes);
57+
58+
var options = {
59+
group_replication_members: members,
60+
innodb_cluster_instances: gr_memberships.cluster_nodes(
61+
mysqld.global.gr_node_host, mysqld.global.cluster_nodes),
62+
gr_id: mysqld.global.gr_id,
63+
innodb_cluster_name: mysqld.global.cluster_name,
64+
mrs_router_id: mysqld.global.mrs_router_id,
65+
};
66+
67+
// prepare the responses for common statements
68+
var common_responses = common_stmts.prepare_statement_responses(
69+
[
70+
"router_set_session_options",
71+
"router_set_gr_consistency_level",
72+
"router_select_cluster_type_v2",
73+
"select_port",
74+
"router_commit",
75+
"router_rollback",
76+
"router_select_schema_version",
77+
"router_check_member_state",
78+
"router_select_members_count",
79+
"router_select_group_membership",
80+
"router_clusterset_present",
81+
"router_select_router_options_view",
82+
"get_routing_guidelines_version",
83+
"get_guidelines_router_info",
84+
"get_routing_guidelines",
85+
"mrs_set_sql_mode",
86+
"mrs_set_meta_provider_role",
87+
"mrs_select_version",
88+
"mrs_select_basedir",
89+
"mrs_set_data_provider_role",
90+
91+
92+
],
93+
options);
94+
95+
var common_responses_regex = common_stmts.prepare_statement_responses_regex(
96+
[
97+
"mrs_select_router_id",
98+
],
99+
options);
100+
101+
var router_select_metadata =
102+
common_stmts.get("router_select_metadata_v2_gr", options);
103+
104+
var router_start_transaction =
105+
common_stmts.get("router_start_transaction", options);
106+
107+
108+
if (common_responses.hasOwnProperty(stmt)) {
109+
return common_responses[stmt];
110+
} else if (
111+
(res = common_stmts.handle_regex_stmt(stmt, common_responses_regex)) !==
112+
undefined) {
113+
return res;
114+
} else if (stmt === "SELECT aurora_version()") {
115+
return {
116+
error: {
117+
code: 1046,
118+
sql_state: "HY001",
119+
message: "No database selected",
120+
}
121+
};
122+
} else if (stmt === router_start_transaction.stmt) {
123+
mysqld.global.transaction_count++;
124+
return router_start_transaction;
125+
} else if (stmt === router_select_metadata.stmt) {
126+
mysqld.global.md_query_count++;
127+
return router_select_metadata;
128+
} else {
129+
return common_stmts.unknown_statement_response(stmt);
130+
}
131+
},
132+
notices: (function() {
133+
return mysqld.global.notices;
134+
})()
135+
})

0 commit comments

Comments
 (0)