Skip to content

Commit 244e5d0

Browse files
committed
BUG#35356006 dba.rebootclusterfromcompleteoutage failed with default group_concat_max_len values
The dba.rebootclusterfromcompleteoutage needs to query the complete set of GTIDs of a specific channel, and the SQL query that does that uses GROUP_CONCAT function to aggregate results. However, the query can fail if the limit set by the group_concat_max_len var is reached. This patch created a version of the query that doesn't need to use the GROUP_CONCAT function, which removes the issue completely. For scenarios where it's still needed, the group_concat_max_len limit was raised to 1GB. Change-Id: I397ddd55aece286cd7a67663ca1705306ead864a
1 parent b120806 commit 244e5d0

File tree

13 files changed

+135
-67
lines changed

13 files changed

+135
-67
lines changed

modules/adminapi/common/instance_pool.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2022, Oracle and/or its affiliates.
2+
* Copyright (c) 2019, 2023, Oracle and/or its affiliates.
33
*
44
* This program is free software; you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License, version 2.0,
@@ -152,6 +152,12 @@ void Instance::prepare_session() {
152152
set_sysvar("collation_connection", std::string("utf8mb4_general_ci"),
153153
mysqlshdk::mysql::Var_qualifier::SESSION);
154154

155+
// Make sure that the limit for 'group_concat_max_len' is increased from the
156+
// default 1024 to 1GB. There's no "correct" value here, hence no constant for
157+
// the variable.
158+
set_sysvar("group_concat_max_len", static_cast<int64_t>(1024 * 1024 * 1024),
159+
mysqlshdk::mysql::Var_qualifier::SESSION);
160+
155161
// Cache the hostname, port, and UUID to avoid errors accessing this data if
156162
// the instance fails during an operation.
157163
get_canonical_address();

modules/adminapi/dba/reboot_cluster_from_complete_outage.cc

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -382,33 +382,36 @@ Reboot_cluster_from_complete_outage::retrieve_instances(
382382
*/
383383
std::shared_ptr<Instance>
384384
Reboot_cluster_from_complete_outage::pick_best_instance_gtid(
385-
const std::vector<std::shared_ptr<Instance>> &instances, bool force,
385+
const std::vector<std::shared_ptr<Instance>> &instances,
386+
bool is_cluster_set_member, bool force,
386387
std::string_view intended_instance) {
387388
std::vector<Instance_gtid_info> instance_gtids;
388389
{
389390
// list of replication channel names that must be considered when comparing
390391
// GTID sets. With ClusterSets, the async channel for secondaries must be
391392
// added here.
392-
const std::vector<std::string> k_known_channel_names = {
393-
"group_replication_applier"};
394-
395393
auto current_session_options = m_target_instance->get_connection_options();
396394

395+
std::vector<std::string> channels;
396+
channels.push_back(mysqlshdk::gr::k_gr_applier_channel);
397+
if (is_cluster_set_member)
398+
channels.push_back(k_clusterset_async_channel_name);
399+
397400
{
398401
Instance_gtid_info info;
399402
info.server = m_target_instance->get_canonical_address();
400-
info.gtid_executed = mysqlshdk::mysql::get_total_gtid_set(
401-
*m_target_instance, k_known_channel_names);
403+
info.gtid_executed =
404+
mysqlshdk::mysql::get_total_gtid_set(*m_target_instance, channels);
402405
instance_gtids.push_back(std::move(info));
403406
}
404407

405408
for (const auto &i : instances) {
406409
Instance_gtid_info info;
407410
info.server = i->get_canonical_address();
408-
info.gtid_executed =
409-
mysqlshdk::mysql::get_total_gtid_set(*i, k_known_channel_names);
411+
info.gtid_executed = mysqlshdk::mysql::get_total_gtid_set(*i, channels);
410412
instance_gtids.push_back(std::move(info));
411413
}
414+
412415
assert(instance_gtids.size() == (instances.size() + 1));
413416
}
414417

@@ -1178,8 +1181,9 @@ std::shared_ptr<Cluster> Reboot_cluster_from_complete_outage::do_run() {
11781181
// pick the seed instance
11791182
std::shared_ptr<Instance> best_instance_gtid;
11801183
{
1181-
best_instance_gtid = pick_best_instance_gtid(
1182-
instances, m_options.get_force(), m_options.get_primary());
1184+
best_instance_gtid =
1185+
pick_best_instance_gtid(instances, m_cs_info.is_member,
1186+
m_options.get_force(), m_options.get_primary());
11831187

11841188
// check if the other instances are compatible in regards to GTID with the
11851189
// (new) seed

modules/adminapi/dba/reboot_cluster_from_complete_outage.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ class Reboot_cluster_from_complete_outage {
7777
* explicit instance and the force option.
7878
*/
7979
std::shared_ptr<Instance> pick_best_instance_gtid(
80-
const std::vector<std::shared_ptr<Instance>> &instances, bool force,
80+
const std::vector<std::shared_ptr<Instance>> &instances,
81+
bool is_cluster_set_member, bool force,
8182
std::string_view intended_instance);
8283

8384
void check_instance_configuration();

mysqlshdk/include/shellcore/base_session.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <functional>
3131
#include <memory>
3232
#include <string>
33+
#include <string_view>
3334
#include <unordered_map>
3435

3536
#include "mysqlshdk/include/scripting/types.h"
@@ -172,7 +173,7 @@ class SHCORE_PUBLIC ShellBaseSession : public shcore::Cpp_object_bridge {
172173
std::function<void(const std::string &, bool exists)> update_schema_cache;
173174

174175
protected:
175-
std::string get_quoted_name(const std::string &name);
176+
std::string get_quoted_name(std::string_view name);
176177
// TODO(rennox): Note that these are now stored on the low level session
177178
// object too, they should be removed from here
178179
mysqlshdk::db::Connection_options _connection_options;

mysqlshdk/libs/db/session.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
namespace mysqlshdk {
3030
namespace db {
3131

32-
Query_attribute::Query_attribute(const std::string &n,
33-
std::unique_ptr<IQuery_attribute_value> v)
34-
: name(n), value(std::move(v)) {}
32+
Query_attribute::Query_attribute(
33+
std::string n, std::unique_ptr<IQuery_attribute_value> v) noexcept
34+
: name(std::move(n)), value(std::move(v)) {}
3535

3636
void ISession::connect(const mysqlshdk::db::Connection_options &data) {
3737
do_connect(data);

mysqlshdk/libs/db/session.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ struct IQuery_attribute_value {
7979
* IQuery_attribute_value interface is used at this level.
8080
*/
8181
struct Query_attribute {
82-
Query_attribute(const std::string &n,
83-
std::unique_ptr<IQuery_attribute_value> v);
82+
Query_attribute(std::string n,
83+
std::unique_ptr<IQuery_attribute_value> v) noexcept;
8484

8585
std::string name;
8686
std::unique_ptr<IQuery_attribute_value> value;

mysqlshdk/libs/mysql/replication.cc

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ std::vector<std::string> get_incoming_channel_names(
392392
}
393393

394394
bool get_channel_status(const mysqlshdk::mysql::IInstance &instance,
395-
const std::string &channel_name,
395+
std::string_view channel_name,
396396
Replication_channel *out_channel) {
397397
auto result = instance.queryf(
398398
std::string(k_base_channel_query) + "WHERE c.channel_name = ?",
@@ -414,7 +414,7 @@ bool get_channel_status(const mysqlshdk::mysql::IInstance &instance,
414414
}
415415

416416
bool get_channel_state(const mysqlshdk::mysql::IInstance &instance,
417-
const std::string &channel_name,
417+
std::string_view channel_name,
418418
Replication_channel::Receiver::State *out_io_state,
419419
Replication_channel::Applier::State *out_sql_state) {
420420
auto result =
@@ -450,7 +450,7 @@ bool get_channel_state(const mysqlshdk::mysql::IInstance &instance,
450450
}
451451

452452
bool get_channel_state(const mysqlshdk::mysql::IInstance &instance,
453-
const std::string &channel_name,
453+
std::string_view channel_name,
454454
Replication_channel::Receiver::State *out_io_state,
455455
Replication_channel::Error *out_io_error,
456456
Replication_channel::Applier::State *out_sql_state,
@@ -803,42 +803,63 @@ std::string get_purged_gtid_set(const mysqlshdk::mysql::IInstance &server) {
803803
}
804804

805805
std::string get_received_gtid_set(const mysqlshdk::mysql::IInstance &server,
806-
const std::string &channel_name) {
806+
std::string_view channel_name) {
807+
// In the query, GTID_SUBTRACT is used, not to subtract GTIDs from the set,
808+
// but to actually clean the set, by removing duplicates, etc.
807809
return server.queryf_one_string(
808810
0, "",
809-
"SELECT GTID_SUBTRACT(GROUP_CONCAT(received_transaction_set), '')"
810-
" FROM performance_schema.replication_connection_status"
811-
" WHERE channel_name = ?",
811+
"SELECT received_transaction_set FROM "
812+
"performance_schema.replication_connection_status WHERE channel_name = ?",
812813
channel_name);
813814
}
814815

815-
std::string get_total_gtid_set(
816-
const mysqlshdk::mysql::IInstance &server,
817-
const std::vector<std::string> &known_channel_names) {
816+
std::string get_total_gtid_set(const mysqlshdk::mysql::IInstance &server,
817+
std::string_view channel_name) {
818+
// In the query, GTID_SUBTRACT is used, not to subtract GTIDs from the set,
819+
// but to actually clean the set, by removing duplicates, etc.
818820
return server.queryf_one_string(
819821
0, "",
820-
"SELECT GTID_SUBTRACT(CONCAT(@@GLOBAL.GTID_EXECUTED, ',', ("
821-
" SELECT GROUP_CONCAT(received_transaction_set)"
822-
" FROM performance_schema.replication_connection_status"
823-
" WHERE channel_name IN (" +
824-
shcore::str_join(known_channel_names, ",", shcore::quote_sql_string) +
825-
"))), '')");
822+
"SELECT GTID_SUBTRACT(CONCAT(@@GLOBAL.GTID_EXECUTED, ',', (SELECT "
823+
"received_transaction_set FROM "
824+
"performance_schema.replication_connection_status WHERE channel_name = "
825+
"?)), '')",
826+
channel_name);
827+
}
828+
829+
std::string get_total_gtid_set(const mysqlshdk::mysql::IInstance &server,
830+
const std::vector<std::string> &channel_names) {
831+
if (channel_names.empty()) return {};
832+
if (channel_names.size() == 1)
833+
return get_total_gtid_set(server, channel_names.front());
834+
835+
// In the query, GTID_SUBTRACT is used, not to subtract GTIDs from the set,
836+
// but to actually clean the set, by removing duplicates, etc.
837+
return server.queryf_one_string(
838+
0, "",
839+
shcore::str_format(
840+
"SELECT GTID_SUBTRACT(CONCAT(@@GLOBAL.GTID_EXECUTED, ',', (SELECT "
841+
"GROUP_CONCAT(received_transaction_set) FROM "
842+
"performance_schema.replication_connection_status WHERE channel_name "
843+
"IN (%s))), '')",
844+
shcore::str_join(channel_names, ",", shcore::quote_sql_string)
845+
.c_str()));
826846
}
827847

828848
Gtid_set_relation compare_gtid_sets(const mysqlshdk::mysql::IInstance &server,
829-
const std::string &gtidset_a,
830-
const std::string &gtidset_b,
849+
std::string_view gtidset_a,
850+
std::string_view gtidset_b,
831851
std::string *out_missing_from_a,
832852
std::string *out_missing_from_b) {
833-
if (out_missing_from_a) *out_missing_from_a = gtidset_b;
834-
if (out_missing_from_b) *out_missing_from_b = gtidset_a;
853+
if (gtidset_a.empty() || gtidset_b.empty()) {
854+
if (out_missing_from_a) *out_missing_from_a = std::string{gtidset_b};
855+
if (out_missing_from_b) *out_missing_from_b = std::string{gtidset_a};
835856

836-
if (gtidset_a.empty()) {
837-
if (gtidset_b.empty())
838-
return Gtid_set_relation::EQUAL;
839-
else
840-
return Gtid_set_relation::CONTAINED;
841-
} else if (gtidset_b.empty()) {
857+
if (gtidset_a.empty()) {
858+
return gtidset_b.empty() ? Gtid_set_relation::EQUAL
859+
: Gtid_set_relation::CONTAINED;
860+
}
861+
862+
assert(gtidset_b.empty());
842863
return Gtid_set_relation::CONTAINS;
843864
}
844865

mysqlshdk/libs/mysql/replication.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include <memory>
2828
#include <string>
29+
#include <string_view>
2930
#include <vector>
3031

3132
#include "mysqlshdk/libs/db/session.h"
@@ -187,7 +188,7 @@ std::string format_status(const Replication_channel &channel,
187188
* @return false if the channel does not exist.
188189
*/
189190
bool get_channel_status(const mysqlshdk::mysql::IInstance &instance,
190-
const std::string &channel_name,
191+
std::string_view channel_name,
191192
Replication_channel *out_channel);
192193

193194
/**
@@ -200,12 +201,12 @@ bool get_channel_status(const mysqlshdk::mysql::IInstance &instance,
200201
* @return [TODO]
201202
*/
202203
bool get_channel_state(const mysqlshdk::mysql::IInstance &instance,
203-
const std::string &channel_name,
204+
std::string_view channel_name,
204205
Replication_channel::Receiver::State *out_io_state,
205206
Replication_channel::Applier::State *out_sql_state);
206207

207208
bool get_channel_state(const mysqlshdk::mysql::IInstance &instance,
208-
const std::string &channel_name,
209+
std::string_view channel_name,
209210
Replication_channel::Receiver::State *out_io_state,
210211
Replication_channel::Error *out_io_error,
211212
Replication_channel::Applier::State *out_sql_state,
@@ -280,19 +281,28 @@ std::string get_purged_gtid_set(const mysqlshdk::mysql::IInstance &server);
280281
* Returns GTID set received by a specific channel.
281282
*/
282283
std::string get_received_gtid_set(const mysqlshdk::mysql::IInstance &server,
283-
const std::string &channel_name);
284+
std::string_view channel_name);
284285

285286
/**
286287
* Returns total GTID set from the server, including received but not yet
287288
* applied.
288289
*
289-
* known_channel_names must contain the list of all channels to consider when
290+
* channel_name the channel to consider when checking for
291+
* received_transaction_set (usually group_replication_applier)
292+
*/
293+
std::string get_total_gtid_set(const mysqlshdk::mysql::IInstance &server,
294+
std::string_view channel_name);
295+
296+
/**
297+
* Returns total GTID set from the server, including received but not yet
298+
* applied.
299+
*
300+
* channel_names must contain the list of all channels to consider when
290301
* checking for received_transaction_set (usually just
291302
* group_replication_applier)
292303
*/
293-
std::string get_total_gtid_set(
294-
const mysqlshdk::mysql::IInstance &server,
295-
const std::vector<std::string> &known_channel_names);
304+
std::string get_total_gtid_set(const mysqlshdk::mysql::IInstance &server,
305+
const std::vector<std::string> &channel_names);
296306

297307
/**
298308
* Returns an upper bound of the number of transactions in the given GTID set.
@@ -324,8 +334,8 @@ enum class Gtid_set_relation {
324334
};
325335

326336
Gtid_set_relation compare_gtid_sets(const mysqlshdk::mysql::IInstance &server,
327-
const std::string &gtidset_a,
328-
const std::string &gtidset_b,
337+
std::string_view gtidset_a,
338+
std::string_view gtidset_b,
329339
std::string *out_missing_from_a = nullptr,
330340
std::string *out_missing_from_b = nullptr);
331341

mysqlshdk/shellcore/base_session.cc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ Query_attribute_store::get_query_attributes(
169169
assert(translator_cb);
170170

171171
std::vector<mysqlshdk::db::Query_attribute> attributes;
172+
attributes.reserve(m_order.size());
172173

173174
for (const auto &name : m_order) {
174175
attributes.emplace_back(name, translator_cb(m_store.at(std::string{name})));
@@ -207,10 +208,13 @@ ShellBaseSession::~ShellBaseSession() { DEBUG_OBJ_DEALLOC(ShellBaseSession); }
207208
std::string &ShellBaseSession::append_descr(std::string &s_out, int /*indent*/,
208209
int /*quote_strings*/) const {
209210
if (!is_open())
210-
s_out.append("<" + class_name() + ":disconnected>");
211+
s_out.append("<").append(class_name()).append(":disconnected>");
211212
else
212-
s_out.append("<" + class_name() + ":" +
213-
uri(mysqlshdk::db::uri::formats::user_transport()) + ">");
213+
s_out.append("<")
214+
.append(class_name())
215+
.append(":")
216+
.append(uri(mysqlshdk::db::uri::formats::user_transport()))
217+
.append(">");
214218
return s_out;
215219
}
216220

@@ -283,16 +287,20 @@ void ShellBaseSession::enable_sql_mode_tracking() {
283287
log_info("Now tracking 'sql_mode' system variable.");
284288
}
285289

286-
std::string ShellBaseSession::get_quoted_name(const std::string &name) {
287-
size_t index = 0;
288-
std::string quoted_name(name);
290+
std::string ShellBaseSession::get_quoted_name(std::string_view name) {
291+
std::string quoted_name;
292+
quoted_name.reserve(name.size() + 2);
289293

290-
while ((index = quoted_name.find("`", index)) != std::string::npos) {
294+
size_t index = 1; // ignore the first '`'
295+
quoted_name.append("`");
296+
quoted_name.append(name);
297+
298+
while ((index = quoted_name.find('`', index)) != std::string::npos) {
291299
quoted_name.replace(index, 1, "``");
292300
index += 2;
293301
}
294302

295-
quoted_name = "`" + quoted_name + "`";
303+
quoted_name.append("`");
296304

297305
return quoted_name;
298306
}

unittest/mysqlshdk/libs/mysql/instance_t.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,4 +1512,21 @@ TEST_F(Instance_test, generate_uuid) {
15121512
instance.generate_uuid();
15131513
}
15141514

1515+
TEST_F(Instance_test, session_limits) {
1516+
mysqlshdk::mysql::Instance instance(_session);
1517+
session
1518+
.expect_query(
1519+
"show SESSION variables "
1520+
"where `variable_name` in ('group_concat_max_len')")
1521+
.then_return({{"show SESSION variables "
1522+
"where `variable_name` in ('group_concat_max_len')",
1523+
{"Variable_name", "Value"},
1524+
{Type::String, Type::Integer},
1525+
{{"group_concat_max_len", "1073741824"}}}});
1526+
auto group_concat_max_len = instance.get_sysvar_int(
1527+
"group_concat_max_len", mysqlshdk::mysql::Var_qualifier::SESSION);
1528+
EXPECT_TRUE(group_concat_max_len);
1529+
EXPECT_EQ(1024 * 1024 * 1024, *group_concat_max_len);
1530+
}
1531+
15151532
} // namespace testing

0 commit comments

Comments
 (0)