Skip to content

Commit 51a14e5

Browse files
committed
WL#14870 BUG#33516540 BUG#33199178 BUG#35993066 BUG#35996854 BUG#35996514 BUG#35997891 BUG#35998172 BUG#36000967 AdminAPI: ReplicaSet rescan, dissolve and describe
This patch adds the following new commands to ReplicaSet: - dissolve() to dissolve the ReplicaSet - rescan() to update the ReplicaSet metadata, which includes updating server ids, adding new instances (part of the topology but not present in the MD, removing obsolete instances (not part of the topology but still in the MD) and storing the current replication accounts. - describe() to output a description (JSON) of the ReplicaSet It also deprecates the "addInstances" and "removeInstances" options from Cluster.rescan() while adding two new ones, "addUnmanaged" and "removeObsolete", replacing the deprecated ones with simpler options. Regarding BUG#33516540, a new check was also added to status() to check if the replication users aren't present in the MD, which might happen if upgrading from a version older than 8.0.27. This causes unused replication accounts to not be removed from the ReplicaSet after removeInstance() removed the corresponding member. This new check in status() informs the user to run rescan() which fixes the issue by storing the account in the MD. Regarding BUG#33199178, which occurs when calling setPrimaryInstance() if the server_id var changed (e.g.: due an OS update), it's also fixed by invoking rescan(), by storing the correct server_id in the MD. Change-Id: Ia3ce3741ed2fbd8b3888475c51090b77614e7b7d
1 parent dfd330e commit 51a14e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3465
-798
lines changed

modules/adminapi/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ file(GLOB adminapi_module_SOURCES
7575
"cluster_set/create_replica_cluster.cc"
7676
"cluster_set/status.cc"
7777
"replica_set/api_options.cc"
78+
"replica_set/describe.cc"
79+
"replica_set/dissolve.cc"
7880
"replica_set/replica_set_impl.cc"
7981
"replica_set/replica_set_status.cc"
82+
"replica_set/rescan.cc"
8083
"cluster/add_instance.cc"
8184
"cluster/add_replica_instance.cc"
8285
"cluster/rejoin_instance.cc"

modules/adminapi/cluster/api_options.cc

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,26 @@
2222
*/
2323
#include "modules/adminapi/cluster/api_options.h"
2424

25-
#include <cinttypes>
26-
#include <utility>
2725
#include <vector>
2826

2927
#include "adminapi/common/api_options.h"
3028
#include "adminapi/common/async_topology.h"
3129
#include "modules/adminapi/common/common.h"
32-
#include "mysqlshdk/include/scripting/type_info/custom.h"
33-
#include "mysqlshdk/include/scripting/type_info/generic.h"
3430
#include "mysqlshdk/include/shellcore/console.h"
35-
#include "mysqlshdk/libs/db/utils_connection.h"
36-
#include "mysqlshdk/libs/utils/utils_file.h"
37-
#include "shellcore/shell_options.h"
38-
#include "utils/utils_net.h"
3931

4032
namespace mysqlsh {
4133
namespace dba {
4234
namespace cluster {
4335

36+
namespace {
37+
void throw_rescan_mixing_exception() {
38+
throw shcore::Exception::argument_error(shcore::str_format(
39+
"Options '%s' and '%s' are mutually exclusive with deprecated options "
40+
"'%s' and '%s'. Mixing either one from both groups isn't allowed.",
41+
kAddUnmanaged, kRemoveObsolete, kAddInstances, kRemoveInstances));
42+
}
43+
} // namespace
44+
4445
const shcore::Option_pack_def<Add_instance_options>
4546
&Add_instance_options::options() {
4647
static const auto opts =
@@ -143,6 +144,8 @@ const shcore::Option_pack_def<Rescan_options> &Rescan_options::options() {
143144
&Rescan_options::set_update_topology_mode)
144145
.optional(kAddInstances, &Rescan_options::set_list_option)
145146
.optional(kRemoveInstances, &Rescan_options::set_list_option)
147+
.optional(kAddUnmanaged, &Rescan_options::set_bool_option)
148+
.optional(kRemoveObsolete, &Rescan_options::set_bool_option)
146149
.optional(kUpgradeCommProtocol,
147150
&Rescan_options::upgrade_comm_protocol)
148151
.optional(kUpdateViewChangeUuid,
@@ -160,8 +163,32 @@ void Rescan_options::set_update_topology_mode(bool /*value*/) {
160163
console->print_info();
161164
}
162165

166+
void Rescan_options::set_bool_option(const std::string &option, bool value) {
167+
if (m_used_deprecated.has_value() && !m_used_deprecated.value())
168+
throw_rescan_mixing_exception();
169+
170+
m_used_deprecated = true;
171+
172+
if (option == kAddUnmanaged)
173+
auto_add = value;
174+
else
175+
auto_remove = value;
176+
}
177+
163178
void Rescan_options::set_list_option(const std::string &option,
164179
const shcore::Value &value) {
180+
if (m_used_deprecated.has_value() && m_used_deprecated.value())
181+
throw_rescan_mixing_exception();
182+
183+
m_used_deprecated = false;
184+
185+
auto console = mysqlsh::current_console();
186+
console->print_info(shcore::str_format(
187+
"The '%s' and '%s' options are deprecated. Please use '%s' and/or '%s' "
188+
"instead.",
189+
kAddInstances, kRemoveInstances, kAddUnmanaged, kRemoveObsolete));
190+
console->print_info();
191+
165192
std::vector<mysqlshdk::db::Connection_options> *instances_list;
166193

167194
// Selects the target list

modules/adminapi/cluster/api_options.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,20 @@ struct Options_options {
8989

9090
struct Rescan_options : public Interactive_option {
9191
static const shcore::Option_pack_def<Rescan_options> &options();
92+
void set_bool_option(const std::string &option, bool value);
9293
void set_list_option(const std::string &option, const shcore::Value &value);
9394
void set_update_topology_mode(bool value);
9495

95-
mysqlshdk::null_bool update_topology_mode;
96+
std::optional<bool> update_topology_mode;
9697
std::vector<mysqlshdk::db::Connection_options> add_instances_list;
9798
std::vector<mysqlshdk::db::Connection_options> remove_instances_list;
9899
bool auto_add = false;
99100
bool auto_remove = false;
100101
bool upgrade_comm_protocol = false;
101-
mysqlshdk::null_bool update_view_change_uuid;
102+
std::optional<bool> update_view_change_uuid;
103+
104+
private:
105+
std::optional<bool> m_used_deprecated;
102106
};
103107

104108
struct Set_primary_instance_options {

modules/adminapi/cluster/describe.cc

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ shcore::Array_t Describe::get_topology() {
8787
shcore::Dictionary_t member = shcore::make_dict();
8888
feed_metadata_info(member, instance_def);
8989

90-
instances_list->push_back(shcore::Value(member));
90+
instances_list->push_back(shcore::Value(std::move(member)));
9191
}
9292

9393
return instances_list;
@@ -96,9 +96,7 @@ shcore::Array_t Describe::get_topology() {
9696
shcore::Dictionary_t Describe::collect_replicaset_description() {
9797
shcore::Dictionary_t ret = shcore::make_dict();
9898

99-
// Set Cluster name and topologyMode
10099
(*ret)["name"] = shcore::Value("default");
101-
102100
(*ret)["topologyMode"] = shcore::Value(m_cluster.get_topology_type());
103101

104102
// Get and set the topology (all instances)
@@ -107,54 +105,13 @@ shcore::Dictionary_t Describe::collect_replicaset_description() {
107105
return ret;
108106
}
109107

110-
shcore::Value Describe::get_default_replicaset_description() {
111-
// Get the Default Cluster description
112-
shcore::Dictionary_t replicaset_dict;
113-
114-
replicaset_dict = collect_replicaset_description();
115-
116-
// Check if the Cluster group session is established to an instance with
117-
// a state different than
118-
// - Online R/W
119-
// - Online R/O
120-
//
121-
// Possibly with the state:
122-
//
123-
// - RECOVERING
124-
// - OFFLINE
125-
// - ERROR
126-
//
127-
// If that's the case, a warning must be added to the resulting JSON object
128-
129-
if (auto group_instance = m_cluster.get_cluster_server()) {
130-
auto state = get_replication_group_state(
131-
*group_instance, get_gr_instance_type(*group_instance));
132-
133-
bool warning = (state.source_state != ManagedInstance::OnlineRW &&
134-
state.source_state != ManagedInstance::OnlineRO);
135-
if (warning) {
136-
std::string warning_msg =
137-
"The cluster description may be inaccurate as it was generated from "
138-
"an instance in ";
139-
warning_msg.append(ManagedInstance::describe(
140-
static_cast<ManagedInstance::State>(state.source_state)));
141-
warning_msg.append(" state");
142-
(*replicaset_dict)["warning"] = shcore::Value(warning_msg);
143-
}
144-
}
145-
146-
return shcore::Value(replicaset_dict);
147-
}
148-
149108
shcore::Value Describe::execute() {
150109
shcore::Dictionary_t dict = shcore::make_dict();
151-
152110
(*dict)["clusterName"] = shcore::Value(m_cluster.get_name());
111+
(*dict)["defaultReplicaSet"] =
112+
shcore::Value(collect_replicaset_description());
153113

154-
// Get the default replicaSet description
155-
(*dict)["defaultReplicaSet"] = get_default_replicaset_description();
156-
157-
return shcore::Value(dict);
114+
return shcore::Value(std::move(dict));
158115
}
159116

160117
void Describe::finish() {

modules/adminapi/cluster/rescan.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ void Rescan::prepare() {
223223
Precondition_checker::k_min_cs_version;
224224

225225
// Validate the usage of the option 'updateViewChangeUuid'
226-
if (m_options.update_view_change_uuid.get_safe() &&
226+
if (m_options.update_view_change_uuid.value_or(false) &&
227227
!m_is_view_change_uuid_supported) {
228228
throw shcore::Exception::argument_error(
229229
"The Cluster cannot be configured to use "
@@ -1052,7 +1052,7 @@ shcore::Value Rescan::execute() {
10521052
"group_replication_view_change_uuid", "");
10531053

10541054
if (view_change_uuid == "AUTOMATIC") {
1055-
if (m_options.update_view_change_uuid.is_null() &&
1055+
if (!m_options.update_view_change_uuid.has_value() &&
10561056
m_cluster->get_lowest_instance_version() <
10571057
k_view_change_uuid_deprecated) {
10581058
console->print_note(
@@ -1073,7 +1073,7 @@ shcore::Value Rescan::execute() {
10731073
"Use the 'updateViewChangeUuid' option to generate and "
10741074
"configure a value for the Cluster.");
10751075
}
1076-
} else if (m_options.update_view_change_uuid.get_safe()) {
1076+
} else if (m_options.update_view_change_uuid.value_or(false)) {
10771077
ensure_view_change_uuid_set();
10781078
}
10791079
} else {

modules/adminapi/common/common.cc

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,32 +1888,15 @@ bool wait_for_gtid_set_safe(const mysqlshdk::mysql::IInstance &target_instance,
18881888
const std::string &gtid_set,
18891889
const std::string &channel_name, int timeout,
18901890
bool cancelable) {
1891-
size_t total_transactions_primary, missing_transactions;
1892-
// The GTID wait will not abort if an error occurs, so calling it without a
1893-
// timeout will just freeze forever. To prevent that, we call the wait with
1894-
// smaller incremental timeouts and wait for errors during that.
1895-
1896-
// wait for at most 10s at a time
1897-
int incremental_timeout = std::min(timeout, 10);
1898-
if (incremental_timeout == 0) incremental_timeout = 2;
1899-
1900-
int time_elapsed = 0;
1901-
19021891
bool stop = false;
1903-
19041892
shcore::Interrupt_handler intr(
19051893
[&stop]() {
19061894
stop = true;
19071895
return true;
19081896
},
19091897
!cancelable);
19101898

1911-
bool sync_res;
1912-
19131899
// Get the Progress_reporting style option in use
1914-
auto progress_reporting = current_shell_options()->get().progress_reporting;
1915-
std::unique_ptr<mysqlshdk::textui::Progress_vt100> progress_bar;
1916-
19171900
const auto update_progress = [](mysqlshdk::textui::Progress_vt100 *bar,
19181901
uint64_t total, size_t missing) {
19191902
bar->set_current(total - missing);
@@ -1923,10 +1906,26 @@ bool wait_for_gtid_set_safe(const mysqlshdk::mysql::IInstance &target_instance,
19231906
// Get the total count of GTID_EXECUTED from the primary
19241907
auto gtid_set_primary =
19251908
mysqlshdk::mysql::Gtid_set::from_normalized_string(gtid_set);
1926-
total_transactions_primary = gtid_set_primary.count();
1909+
auto total_transactions_primary =
1910+
static_cast<size_t>(gtid_set_primary.count());
1911+
1912+
// calculate missing transactions
1913+
const auto calc_missing_transactions = [&target_instance,
1914+
&gtid_set_primary]() -> size_t {
1915+
auto gtid_set_target =
1916+
mysqlshdk::mysql::Gtid_set::from_gtid_executed(target_instance);
1917+
1918+
return mysqlshdk::mysql::estimate_gtid_set_size(
1919+
gtid_set_primary.subtract(gtid_set_target, target_instance));
1920+
};
19271921

19281922
using Progress_reporting = Shell_options::Storage::Progress_reporting;
19291923

1924+
auto progress_reporting = current_shell_options()->get().progress_reporting;
1925+
1926+
std::unique_ptr<mysqlshdk::textui::Progress_vt100> progress_bar;
1927+
shcore::Scoped_callback end_progress_bar;
1928+
19301929
if (progress_reporting == Progress_reporting::PROGRESSBAR) {
19311930
// Init the progress bar
19321931
progress_bar = std::make_unique<mysqlshdk::textui::Progress_vt100>(0);
@@ -1937,43 +1936,42 @@ bool wait_for_gtid_set_safe(const mysqlshdk::mysql::IInstance &target_instance,
19371936
// Always terminate the progress bar when going out of the scope of the
19381937
// function. That must happen regardless if the function ended successfully
19391938
// or not, or because of an exception, to ensure the output is cleared out.
1940-
shcore::Scoped_callback end_progress_bar(
1941-
[&progress_bar]() { progress_bar->end(); });
1939+
end_progress_bar =
1940+
shcore::Scoped_callback([&progress_bar]() { progress_bar->end(); });
19421941

19431942
// Update the progress bar to show the current state of the transactions
19441943
// missing
1945-
auto gtid_set_target =
1946-
mysqlshdk::mysql::Gtid_set::from_gtid_executed(target_instance);
1947-
missing_transactions = mysqlshdk::mysql::estimate_gtid_set_size(
1948-
gtid_set_primary.subtract(gtid_set_target, target_instance));
1949-
19501944
update_progress(progress_bar.get(), total_transactions_primary,
1951-
missing_transactions);
1945+
calc_missing_transactions());
19521946
}
19531947

1948+
// The GTID wait will not abort if an error occurs, so calling it without a
1949+
// timeout will just freeze forever. To prevent that, we call the wait with
1950+
// smaller incremental timeouts and wait for errors during that.
1951+
//
1952+
// wait for at most 10s at a time
1953+
int incremental_timeout = std::min(timeout, 10);
1954+
if (incremental_timeout == 0) incremental_timeout = 2;
1955+
1956+
bool sync_res;
1957+
int time_elapsed = 0;
19541958
do {
19551959
// Every minute, get the executed gtid_set at the target instance to check
19561960
// how many transactions are still missing to provide that info to the user
19571961
if (time_elapsed != 0 && time_elapsed % 60 == 0) {
1958-
auto gtid_set_target =
1959-
mysqlshdk::mysql::Gtid_set::from_gtid_executed(target_instance);
1960-
1961-
missing_transactions = mysqlshdk::mysql::estimate_gtid_set_size(
1962-
gtid_set_primary.subtract(gtid_set_target, target_instance));
1963-
19641962
switch (progress_reporting) {
19651963
case Progress_reporting::PROGRESSBAR: {
1964+
assert(progress_bar);
19661965
update_progress(progress_bar.get(), total_transactions_primary,
1967-
missing_transactions);
1966+
calc_missing_transactions());
19681967
break;
19691968
}
19701969
case Progress_reporting::SIMPLE: {
19711970
// Simple progress reporting, just print info about the transactions
19721971
// left
1973-
mysqlsh::current_console()->print_info(
1974-
"** " + std::to_string(missing_transactions) + " of " +
1975-
std::to_string(total_transactions_primary) +
1976-
" transactions left");
1972+
mysqlsh::current_console()->print_info(shcore::str_format(
1973+
"** %zu of %zu transactions left", calc_missing_transactions(),
1974+
total_transactions_primary));
19771975
break;
19781976
}
19791977
default:
@@ -1996,6 +1994,7 @@ bool wait_for_gtid_set_safe(const mysqlshdk::mysql::IInstance &target_instance,
19961994
} else {
19971995
// wait succeeded, get out of here
19981996
if (progress_reporting == Progress_reporting::PROGRESSBAR) {
1997+
assert(progress_bar);
19991998
update_progress(progress_bar.get(), total_transactions_primary, 0);
20001999
}
20012000
break;

modules/adminapi/common/common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ inline constexpr const char kForce[] = "force";
261261
inline constexpr const char kAdoptFromGR[] = "adoptFromGR";
262262
inline constexpr const char kAddInstances[] = "addInstances";
263263
inline constexpr const char kRemoveInstances[] = "removeInstances";
264+
inline constexpr const char kAddUnmanaged[] = "addUnmanaged";
265+
inline constexpr const char kRemoveObsolete[] = "removeObsolete";
264266
inline constexpr const char kRejoinInstances[] = "rejoinInstances";
265267
inline constexpr const char kWaitRecovery[] = "waitRecovery";
266268
inline constexpr const char kRecoveryProgress[] = "recoveryProgress";

modules/adminapi/common/metadata_storage.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,18 @@ void MetadataStorage::update_instance_repl_account(
13231323
execute_sql(query);
13241324
}
13251325

1326+
void MetadataStorage::update_instance_uuid(Cluster_id cluster_id,
1327+
Instance_id instance_id,
1328+
std::string_view instance_uuid) {
1329+
auto query =
1330+
"UPDATE mysql_innodb_cluster_metadata.instances SET mysql_server_uuid = "
1331+
"? WHERE (cluster_id = ?) AND (instance_id = ?);"_sql
1332+
<< instance_uuid << cluster_id << instance_id;
1333+
query.done();
1334+
1335+
execute_sql(query);
1336+
}
1337+
13261338
std::pair<std::string, std::string> MetadataStorage::get_instance_repl_account(
13271339
const std::string &instance_uuid, Cluster_type type,
13281340
Replica_type replica_type) {
@@ -1594,7 +1606,7 @@ size_t MetadataStorage::iterate_recovery_account(
15941606
return num_accounts;
15951607
}
15961608

1597-
void MetadataStorage::remove_instance(const std::string &instance_address,
1609+
void MetadataStorage::remove_instance(std::string_view instance_address,
15981610
Transaction_undo *undo) {
15991611
// Remove the instance
16001612
auto query = ("DELETE FROM mysql_innodb_cluster_metadata.instances "

0 commit comments

Comments
 (0)