Skip to content

Commit 3b094c9

Browse files
committed
BUG#35410360 Admin API X Protocol Port changes
The AdminAPI stores in the Metadata schema information about each instance's endpoints, including the X Plugin port endpoint. This particular endpoint is used by Router when the GR notifications are enabled. If, for some reason, the X Port is changed on a Cluster member and the member is restarted, the information stored in the Metadata will be outdated and the AdminAPI is not detecting it or attempting to correct it. This can lead to MySQL Router not being able to connect for the GR notifications or connect to a wrong Cluster. This patch fixes that by including a check for a possible change of the X Port in Cluster.status() and the possibility of updating the Metadata schema with Cluster.rescan(). Change-Id: I46c96fd397b1f7dd48c325448adb2f1437385da4
1 parent e8711e1 commit 3b094c9

File tree

11 files changed

+361
-54
lines changed

11 files changed

+361
-54
lines changed

modules/adminapi/cluster/rescan.cc

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "modules/adminapi/common/metadata_storage.h"
3232
#include "modules/adminapi/common/preconditions.h"
3333
#include "modules/adminapi/common/validations.h"
34+
#include "mysqlshdk/include/scripting/types.h"
3435
#include "mysqlshdk/include/shellcore/console.h"
3536
#include "mysqlshdk/libs/config/config.h"
3637
#include "mysqlshdk/libs/config/config_server_handler.h"
@@ -43,6 +44,8 @@ namespace mysqlsh {
4344
namespace dba {
4445
namespace cluster {
4546

47+
constexpr const char *k_xplugin_disabled = "<disabled>";
48+
4649
Rescan::Rescan(const Rescan_options &options, Cluster_impl *cluster)
4750
: m_options(options), m_cluster(cluster) {
4851
assert(cluster);
@@ -251,38 +254,73 @@ void Rescan::check_mismatched_hostnames_addresses(
251254

252255
auto address = instance->get_canonical_address();
253256

257+
std::optional<int> xport = instance->get_xport();
258+
254259
auto res = instance->query(
255260
"select @@hostname, @@report_host, @@port, @@report_port");
256261
auto row = res->fetch_one();
257262

258263
log_debug(
259264
"Hostname metadata check for %s: address=%s @@hostname=%s "
260-
"@@report_host=%s @@port=%s @@report_port=%s md.instance_id=%u "
261-
"md.endpoint=%s md.address=%s md.label=%s md.xendpoint=%s "
262-
"md.grendpoint=%s member_id=%s member_host=%s member_port=%i",
265+
"@@report_host=%s @@port=%s @@report_port=%s @@mysqlx_port=%s "
266+
" md.instance_id=%u md.endpoint=%s md.address=%s md.label=%s "
267+
"md.xendpoint=%s md.grendpoint=%s member_id=%s member_host=%s "
268+
"member_port=%i",
263269
instance->get_uuid().c_str(), address.c_str(),
264270
row->get_as_string(0).c_str(), row->get_as_string(1).c_str(),
265271
row->get_as_string(2).c_str(), row->get_as_string(3).c_str(),
272+
xport.has_value() ? std::to_string(*xport).c_str() : "null",
266273
info.first.id, info.first.endpoint.c_str(),
267274
info.first.address.c_str(), info.first.label.c_str(),
268275
info.first.xendpoint.c_str(), info.first.grendpoint.c_str(),
269276
info.second.uuid.c_str(), info.second.host.c_str(),
270277
info.second.port);
271278

279+
bool xport_equal = false;
280+
bool address_equal = false;
281+
std::string x_address;
282+
283+
auto canonical_hostname = instance->get_canonical_hostname();
284+
285+
if (xport.has_value()) {
286+
x_address =
287+
mysqlshdk::utils::make_host_and_port(canonical_hostname, *xport);
288+
}
289+
290+
if (mysqlshdk::utils::are_endpoints_equal(info.first.xendpoint,
291+
x_address)) {
292+
xport_equal = true;
293+
}
294+
272295
if (grendpoint_valid &&
273296
mysqlshdk::utils::are_endpoints_equal(info.first.endpoint,
274297
address) &&
275298
mysqlshdk::utils::are_endpoints_equal(info.first.address,
276299
address)) {
277-
return true;
300+
address_equal = true;
278301
}
279302

280-
instances->emplace_back(shcore::Value(
281-
shcore::make_dict("member_id", shcore::Value(instance->get_uuid()),
282-
"id", shcore::Value(info.first.id), "label",
283-
shcore::Value(info.first.label), "host",
284-
shcore::Value(instance->get_canonical_address()),
285-
"old_host", shcore::Value(info.first.endpoint))));
303+
if (xport_equal && address_equal) return true;
304+
305+
shcore::Dictionary_t dict = shcore::make_dict();
306+
307+
dict->set("member_id", shcore::Value(instance->get_uuid()));
308+
dict->set("id", shcore::Value(info.first.id));
309+
dict->set("label", shcore::Value(info.first.label));
310+
dict->set("host", shcore::Value(instance->get_canonical_address()));
311+
dict->set(
312+
"xport_endpoint",
313+
shcore::Value(x_address.empty() ? k_xplugin_disabled : x_address));
314+
315+
instances->emplace_back(shcore::Value(dict));
316+
317+
if (!address_equal) {
318+
dict->set("old_host", shcore::Value(info.first.endpoint));
319+
}
320+
321+
if (!xport_equal) {
322+
dict->set("old_xport_endpoint", shcore::Value(info.first.xendpoint));
323+
}
286324

287325
return true;
288326
},
@@ -615,6 +653,7 @@ void Rescan::update_metadata_for_instances(
615653
for (const auto &instance : *instance_list) {
616654
auto instance_map = instance.as_map();
617655
std::string label;
656+
std::string xport_endpoint_str;
618657

619658
std::string instance_address = instance_map->get_string("host");
620659
// Report that an obsolete instance was found (in the metadata).
@@ -628,8 +667,7 @@ void Rescan::update_metadata_for_instances(
628667
instance_map->get_string("member_id").c_str()));
629668
} else {
630669
// member address changed
631-
if (instance_map->get_string("old_host") !=
632-
instance_map->get_string("host")) {
670+
if (instance_map->has_key("old_host")) {
633671
console->print_info(shcore::str_format(
634672
"The instance '%s' is part of the cluster but its "
635673
"reported address has changed. "
@@ -641,8 +679,22 @@ void Rescan::update_metadata_for_instances(
641679

642680
// update instance label if it's the default value
643681
if (instance_map->get_string("label") ==
644-
instance_map->get_string("old_host"))
682+
instance_map->get_string("old_host")) {
645683
label = instance_map->get_string("host");
684+
}
685+
686+
// xport has changed
687+
if (instance_map->has_key("old_xport_endpoint")) {
688+
xport_endpoint_str = instance_map->get_string("xport_endpoint");
689+
690+
console->print_info(shcore::str_format(
691+
"The instance '%s' is part of the Cluster but the reported X "
692+
"plugin port has changed. "
693+
"Old xport: %s. Current xport: %s.",
694+
instance_address.c_str(),
695+
instance_map->get_string("old_xport_endpoint").c_str(),
696+
xport_endpoint_str.c_str()));
697+
}
646698
}
647699

648700
// Decide to add/remove instance based on given operation options.
@@ -651,6 +703,13 @@ void Rescan::update_metadata_for_instances(
651703

652704
update_metadata_for_instance(instance_cnx_opts,
653705
instance_map->get_uint("id"), label);
706+
707+
// The xplugin was disabled, remove the endpoint from the metadata
708+
if (xport_endpoint_str == k_xplugin_disabled) {
709+
m_cluster->get_metadata_storage()->update_instance_addresses(
710+
instance_map->get_string("member_id"), "mysqlX",
711+
shcore::Value::Null());
712+
}
654713
}
655714
}
656715

modules/adminapi/cluster/status.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "modules/adminapi/cluster/status.h"
2525

2626
#include <algorithm>
27+
#include <optional>
28+
#include <string>
2729

2830
#include "modules/adminapi/cluster/api_options.h"
2931
#include "modules/adminapi/cluster_set/cluster_set_impl.h"
@@ -954,6 +956,25 @@ void check_host_metadata(shcore::Array_t issues, Instance *instance,
954956
instance_md.md.endpoint + ", actual=" + address +
955957
"). Use rescan() to update the metadata."));
956958
}
959+
960+
// Check if the instance's X plugin port matches the metadata
961+
std::optional<int> xport = instance->get_xport();
962+
std::string x_address;
963+
964+
if (xport.has_value()) {
965+
x_address = mysqlshdk::utils::make_host_and_port(
966+
instance->get_canonical_hostname(), *xport);
967+
}
968+
969+
if (!mysqlshdk::utils::are_endpoints_equal(instance_md.md.xendpoint,
970+
x_address)) {
971+
issues->push_back(shcore::Value(
972+
"ERROR: Metadata for this instance does not match "
973+
"X plugin port reported by instance (metadata=" +
974+
instance_md.md.xendpoint +
975+
", actual=" + (x_address.empty() ? "<disabled>" : x_address) +
976+
"). Use rescan() to update the metadata."));
977+
}
957978
}
958979

959980
void check_transaction_size_limit(shcore::Array_t issues, Instance *instance,

modules/adminapi/common/metadata_storage.cc

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,22 +1048,35 @@ bool MetadataStorage::query_instance_attribute(std::string_view uuid,
10481048
return false;
10491049
}
10501050

1051-
void MetadataStorage::update_instance_attribute(std::string_view uuid,
1052-
std::string_view attribute,
1053-
const shcore::Value &value,
1054-
Transaction_undo *undo) {
1051+
void MetadataStorage::update_instance_metadata(std::string_view uuid,
1052+
Instance_column_md column,
1053+
std::string_view field,
1054+
const shcore::Value &value,
1055+
Transaction_undo *undo) {
1056+
std::string column_str;
1057+
1058+
switch (column) {
1059+
case Instance_column_md::ATTRIBUTES:
1060+
column_str = "attributes";
1061+
break;
1062+
case Instance_column_md::ADDRESSES:
1063+
column_str = "addresses";
1064+
break;
1065+
}
1066+
10551067
if (undo) {
10561068
undo->add_snapshot_for_update(
1057-
"mysql_innodb_cluster_metadata.instances", "attributes",
1058-
*get_md_server(), shcore::sqlformat("mysql_server_uuid = ?", uuid));
1069+
"mysql_innodb_cluster_metadata.instances", column_str, *get_md_server(),
1070+
shcore::sqlformat("mysql_server_uuid = ?", uuid));
10591071
}
10601072

10611073
if (value) {
10621074
auto stmt = shcore::str_format(
1063-
"UPDATE mysql_innodb_cluster_metadata.instances SET attributes = "
1064-
"json_set(attributes, '$.%.*s', CAST(? as JSON)) WHERE "
1075+
"UPDATE mysql_innodb_cluster_metadata.instances SET %s = "
1076+
"json_set(%s, '$.%.*s', CAST(? as JSON)) WHERE "
10651077
"mysql_server_uuid = ?",
1066-
static_cast<int>(attribute.length()), attribute.data());
1078+
column_str.c_str(), column_str.c_str(),
1079+
static_cast<int>(field.length()), field.data());
10671080

10681081
shcore::sqlstring query(stmt, 0);
10691082
query << value.repr() << uuid;
@@ -1072,9 +1085,10 @@ void MetadataStorage::update_instance_attribute(std::string_view uuid,
10721085
execute_sql(query);
10731086
} else {
10741087
auto stmt = shcore::str_format(
1075-
"UPDATE mysql_innodb_cluster_metadata.instances SET attributes = "
1076-
"json_remove(attributes, '$.%.*s') WHERE mysql_server_uuid = ?",
1077-
static_cast<int>(attribute.length()), attribute.data());
1088+
"UPDATE mysql_innodb_cluster_metadata.instances SET %s = "
1089+
"json_remove(%s, '$.%.*s') WHERE mysql_server_uuid = ?",
1090+
column_str.c_str(), column_str.c_str(),
1091+
static_cast<int>(field.length()), field.data());
10781092

10791093
shcore::sqlstring query(stmt, 0);
10801094
query << uuid;
@@ -1084,6 +1098,22 @@ void MetadataStorage::update_instance_attribute(std::string_view uuid,
10841098
}
10851099
}
10861100

1101+
void MetadataStorage::update_instance_attribute(std::string_view uuid,
1102+
std::string_view attribute,
1103+
const shcore::Value &value,
1104+
Transaction_undo *undo) {
1105+
update_instance_metadata(uuid, Instance_column_md::ATTRIBUTES, attribute,
1106+
value, undo);
1107+
}
1108+
1109+
void MetadataStorage::update_instance_addresses(std::string_view uuid,
1110+
std::string_view address,
1111+
const shcore::Value &value,
1112+
Transaction_undo *undo) {
1113+
update_instance_metadata(uuid, Instance_column_md::ADDRESSES, address, value,
1114+
undo);
1115+
}
1116+
10871117
void MetadataStorage::set_instance_tag(const std::string &uuid,
10881118
const std::string &tagname,
10891119
const shcore::Value &value) {

modules/adminapi/common/metadata_storage.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ inline constexpr const char *kNotifyDataClusterSetId = "CLUSTER_SET_ID";
5252

5353
enum class Instance_type { GROUP_MEMBER, ASYNC_MEMBER, READ_REPLICA, NONE };
5454

55+
enum class Instance_column_md { ATTRIBUTES, ADDRESSES };
56+
5557
std::string to_string(const Instance_type instance_type);
5658
Instance_type to_instance_type(const std::string &instance_type);
5759

@@ -276,6 +278,11 @@ class MetadataStorage {
276278
const shcore::Value &value,
277279
Transaction_undo *undo = nullptr);
278280

281+
void update_instance_addresses(std::string_view uuid,
282+
std::string_view address,
283+
const shcore::Value &value,
284+
Transaction_undo *undo = nullptr);
285+
279286
/**
280287
* Update the information on the metadata about a tag set on the instance with
281288
* the given uuid.
@@ -699,6 +706,12 @@ class MetadataStorage {
699706

700707
bool cluster_sets_supported() const;
701708

709+
void update_instance_metadata(std::string_view uuid,
710+
Instance_column_md column,
711+
std::string_view field,
712+
const shcore::Value &value,
713+
Transaction_undo *undo);
714+
702715
friend class Transaction;
703716

704717
std::shared_ptr<Instance> m_md_server;

modules/adminapi/common/sql.cc

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -265,20 +265,13 @@ std::vector<std::pair<std::string, int>> get_open_sessions(
265265
Instance_metadata query_instance_info(
266266
const mysqlshdk::mysql::IInstance &instance, bool validate_gr_endpoint,
267267
Instance_type instance_type) {
268-
int xport = -1;
268+
std::optional<int> xport;
269269
std::string local_gr_address;
270270

271271
// Get the required data from the joining instance to store in the metadata
272272

273273
// Get the MySQL X port.
274-
try {
275-
xport = instance.queryf_one_int(0, -1, "SELECT @@mysqlx_port");
276-
} catch (const std::exception &e) {
277-
log_info(
278-
"The X plugin is not enabled on instance '%s'. No value will be "
279-
"assumed for the X protocol address.",
280-
instance.descr().c_str());
281-
}
274+
xport = instance.get_xport();
282275

283276
if (instance_type == Instance_type::GROUP_MEMBER) {
284277
// Get the local GR host data.
@@ -311,9 +304,10 @@ Instance_metadata query_instance_info(
311304

312305
instance_def.address = instance.get_canonical_address();
313306
instance_def.endpoint = instance_def.address;
314-
if (xport != -1)
307+
if (xport.has_value()) {
315308
instance_def.xendpoint = mysqlshdk::utils::make_host_and_port(
316-
instance.get_canonical_hostname(), xport);
309+
instance.get_canonical_hostname(), *xport);
310+
}
317311

318312
if (instance_type == Instance_type::GROUP_MEMBER) {
319313
instance_def.grendpoint = local_gr_address;

mysqlshdk/libs/mysql/instance.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,24 @@ int Instance::get_canonical_port() const {
9898
return m_port;
9999
}
100100

101+
std::optional<int> Instance::get_xport() const {
102+
if (!m_xport.has_value()) {
103+
try {
104+
auto result = query("SELECT @@mysqlx_port");
105+
auto row = result->fetch_one();
106+
107+
m_xport = row->get_int(0);
108+
} catch (const std::exception &) {
109+
log_info("The X plugin is not enabled on instance '%s'.",
110+
descr().c_str());
111+
112+
m_xport = std::nullopt;
113+
}
114+
}
115+
116+
return m_xport;
117+
}
118+
101119
std::string Instance::get_canonical_address() const {
102120
// returns the canonical address that should be used to reach this instance in
103121
// the format: canonical_hostname + ':' + canonical_port

mysqlshdk/libs/mysql/instance.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class IInstance {
9090
virtual std::string descr() const = 0;
9191
virtual std::string get_canonical_hostname() const = 0;
9292
virtual int get_canonical_port() const = 0;
93+
virtual std::optional<int> get_xport() const = 0;
9394
/**
9495
* @brief returns the canonical address that should be used to reach this
9596
* instance in the format: canonical_hostname + ':' + canonical_port
@@ -373,6 +374,7 @@ class Instance : public IInstance {
373374
std::string get_canonical_hostname() const override;
374375
std::string get_canonical_address() const override;
375376
int get_canonical_port() const override;
377+
std::optional<int> get_xport() const override;
376378

377379
const std::string &get_uuid() const override;
378380
uint32_t get_id() const override;
@@ -528,6 +530,7 @@ class Instance : public IInstance {
528530
mutable std::string m_group_name;
529531
mutable std::string m_hostname;
530532
mutable int m_port = 0;
533+
mutable std::optional<int> m_xport;
531534
mutable uint32_t m_server_id = 0;
532535
int m_sql_binlog_suppress_count = 0;
533536
Warnings_callback m_warnings_callback = nullptr;

0 commit comments

Comments
 (0)