Skip to content

Commit 3bc4362

Browse files
author
Arun Kuruvila
committed
Bug#32843447: GTID_PURGED IS GETTING EMPTY AND GTID_EXECUTED
IS GETTING UPDATED AFTER RESTARTING MYSQL Description: The state of the server in regards to GTIDs is shared between the binary logs and the `mysql.gtid_executed` table and both are needed in different scenarios to keep the consistency of the state. Analysis: Issue in the reported case is the following: 1. The dump returned by `mysqldump` includes the `SET GTID_PURGED` set to the value of `GTID_EXECUTED` and the dropping of the `mysql` schema, in that order. 2. `RESET MASTER` clears the server state (GTID state included), meaning there are no binary logs. 3. The restore of the dump does the following, in the described order: a. Executes `SET GTID_PURGED`, which writes the GTID state to the `mysql.gtid_executed` table. b. It drops the `mysql` schema, which contains the `gtid_executed` table. Since the produced dump file doesn't restore the contents of the `mysql.gtid_executed` table, the changes in 3.a. get undone. Since there are no binary logs, the data added to `mysql.gtid_executed` was the only persisted value of the GTID state, at the point just after `SET GTID_PURGED` execution. Fix : The issue is fixed in 2 parts : 1. Added a new option, '--skip-mysql-schema', in mysqldump which when set should not allow mysql schema to be dropped. 2. Updated the sequence in which the `GTID_PURGED` is set in the dump file. RB#26690
1 parent d7e5a18 commit 3bc4362

File tree

5 files changed

+268
-63
lines changed

5 files changed

+268
-63
lines changed

client/client_priv.h

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ enum options_client
114114
OPT_CONNECTION_SERVER_ID,
115115
OPT_TLS_VERSION,
116116
OPT_SSL_MODE,
117+
OPT_SKIP_MYSQL_SCHEMA,
117118
/* Add new option above this */
118119
OPT_MAX_CLIENT_OPTION
119120
};

client/mysqldump.c

+81-63
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0,
120120
opt_include_master_host_port= 0,
121121
opt_events= 0, opt_comments_used= 0,
122122
opt_alltspcs=0, opt_notspcs= 0, opt_drop_trigger= 0,
123-
opt_secure_auth= TRUE;
123+
opt_skip_mysql_schema=0, opt_secure_auth= TRUE;
124124
static my_bool insert_pat_inited= 0, debug_info_flag= 0, debug_check_flag= 0;
125125
static ulong opt_max_allowed_packet, opt_net_buffer_length;
126126
static MYSQL mysql_connection,*mysql=0;
@@ -524,6 +524,9 @@ static struct my_option my_long_options[] =
524524
{"dump-date", OPT_DUMP_DATE, "Put a dump date to the end of the output.",
525525
&opt_dump_date, &opt_dump_date, 0,
526526
GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
527+
{"skip_mysql_schema", OPT_SKIP_MYSQL_SCHEMA, "Skip adding DROP DATABASE for mysql schema.",
528+
&opt_skip_mysql_schema, &opt_skip_mysql_schema, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
529+
0},
527530
{"skip-opt", OPT_SKIP_OPTIMIZATION,
528531
"Disable --opt. Disables --add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, and --disable-keys.",
529532
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
@@ -584,9 +587,9 @@ static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row,
584587
int string_value);
585588
static int dump_selected_tables(char *db, char **table_names, int tables);
586589
static int dump_all_tables_in_db(char *db);
587-
static int init_dumping_views(char *);
588-
static int init_dumping_tables(char *);
589-
static int init_dumping(char *, int init_func(char*));
590+
static int init_dumping_views(char *, my_bool);
591+
static int init_dumping_tables(char *, my_bool);
592+
static int init_dumping(char *, int init_func(char*, my_bool));
590593
static int dump_databases(char **);
591594
static int dump_all_databases();
592595
static char *quote_name(const char *name, char *buff, my_bool force);
@@ -4735,12 +4738,14 @@ View Specific database initalization.
47354738
SYNOPSIS
47364739
init_dumping_views
47374740
qdatabase quoted name of the database
4741+
is_mysql_db TRUE if the db is mysql, else FALSE
47384742
47394743
RETURN VALUES
47404744
0 Success.
47414745
1 Failure.
47424746
*/
4743-
int init_dumping_views(char *qdatabase MY_ATTRIBUTE((unused)))
4747+
int init_dumping_views(char *qdatabase MY_ATTRIBUTE((unused)),
4748+
my_bool is_mysql_db MY_ATTRIBUTE((unused)))
47444749
{
47454750
return 0;
47464751
} /* init_dumping_views */
@@ -4752,13 +4757,14 @@ Table Specific database initalization.
47524757
SYNOPSIS
47534758
init_dumping_tables
47544759
qdatabase quoted name of the database
4760+
is_mysql_db TRUE if the db is mysql, else FALSE
47554761
47564762
RETURN VALUES
47574763
0 Success.
47584764
1 Failure.
47594765
*/
47604766

4761-
int init_dumping_tables(char *qdatabase)
4767+
int init_dumping_tables(char *qdatabase, my_bool is_mysql_db)
47624768
{
47634769
DBUG_ENTER("init_dumping_tables");
47644770

@@ -4775,7 +4781,7 @@ int init_dumping_tables(char *qdatabase)
47754781
if (mysql_query(mysql, qbuf) || !(dbinfo = mysql_store_result(mysql)))
47764782
{
47774783
/* Old server version, dump generic CREATE DATABASE */
4778-
if (opt_drop_database)
4784+
if (opt_drop_database && (!opt_skip_mysql_schema || !is_mysql_db))
47794785
fprintf(md_result_file,
47804786
"\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n",
47814787
qdatabase);
@@ -4785,7 +4791,7 @@ int init_dumping_tables(char *qdatabase)
47854791
}
47864792
else
47874793
{
4788-
if (opt_drop_database)
4794+
if (opt_drop_database && (!opt_skip_mysql_schema || !is_mysql_db))
47894795
fprintf(md_result_file,
47904796
"\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n",
47914797
qdatabase);
@@ -4801,7 +4807,7 @@ int init_dumping_tables(char *qdatabase)
48014807
} /* init_dumping_tables */
48024808

48034809

4804-
static int init_dumping(char *database, int init_func(char*))
4810+
static int init_dumping(char *database, int init_func(char*, my_bool))
48054811
{
48064812
if (is_ndbinfo(mysql, database))
48074813
{
@@ -4825,14 +4831,15 @@ static int init_dumping(char *database, int init_func(char*))
48254831
char *qdatabase= quote_name(database,quoted_database_buf,opt_quoted);
48264832
my_bool freemem= FALSE;
48274833
char const* text= fix_identifier_with_newline(qdatabase, &freemem);
4834+
my_bool is_mysql_db= !my_strcasecmp(charset_info, database, "mysql");
48284835

48294836
print_comment(md_result_file, 0, "\n--\n-- Current Database: %s\n--\n",
48304837
text);
48314838
if (freemem)
48324839
my_free((void*)text);
48334840

48344841
/* Call the view or table specific function */
4835-
init_func(qdatabase);
4842+
init_func(qdatabase, is_mysql_db);
48364843

48374844
fprintf(md_result_file,"\nUSE %s;\n", qdatabase);
48384845
check_io(md_result_file);
@@ -5839,24 +5846,20 @@ static int replace(DYNAMIC_STRING *ds_str,
58395846
58405847
@note: md_result_file should have been opened, before
58415848
this function is called.
5842-
5843-
@param[in] flag If FALSE, disable binlog.
5844-
If TRUE and binlog disabled previously,
5845-
restore the session binlog.
58465849
*/
58475850

5848-
static void set_session_binlog(my_bool flag)
5851+
static void set_session_binlog()
58495852
{
58505853
static my_bool is_binlog_disabled= FALSE;
58515854

5852-
if (!flag && !is_binlog_disabled)
5855+
if (!is_binlog_disabled)
58535856
{
58545857
fprintf(md_result_file,
58555858
"SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;\n");
58565859
fprintf(md_result_file, "SET @@SESSION.SQL_LOG_BIN= 0;\n");
58575860
is_binlog_disabled= 1;
58585861
}
5859-
else if (flag && is_binlog_disabled)
5862+
else
58605863
{
58615864
fprintf(md_result_file,
58625865
"SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;\n");
@@ -5894,7 +5897,7 @@ static my_bool add_set_gtid_purged(MYSQL *mysql_con)
58945897
{
58955898
if (opt_comments)
58965899
fprintf(md_result_file,
5897-
"\n--\n-- GTID state at the beginning of the backup \n--\n\n");
5900+
"\n--\n-- GTID state at the end of the backup \n--\n\n");
58985901

58995902
fprintf(md_result_file,"SET @@GLOBAL.GTID_PURGED='");
59005903

@@ -5917,79 +5920,98 @@ static my_bool add_set_gtid_purged(MYSQL *mysql_con)
59175920

59185921
/**
59195922
This function processes the opt_set_gtid_purged option.
5920-
This function also calls set_session_binlog() function before
5921-
setting the SET @@GLOBAL.GTID_PURGED in the output.
5923+
This function when called with the flag as FALSE, just
5924+
disables the binlog by calling set_session_binlog().
5925+
Later when this function is called with the flag as TRUE,
5926+
SET @@GLOBAL.GTID_PURGED is written in the output and the
5927+
session binlog is restored if disabled previously.
59225928
59235929
@param[in] mysql_con the connection to the server
5930+
@param[in] flag If FALSE, just disable binlog and not
5931+
set the gtid purged as it will be set
5932+
at a later point of time.
5933+
If TRUE, set the gtid purged and
5934+
restore the session binlog if disabled
5935+
previously.
59245936
59255937
@retval FALSE successful according to the value
59265938
of opt_set_gtid_purged.
59275939
@retval TRUE fail.
59285940
*/
59295941

5930-
static my_bool process_set_gtid_purged(MYSQL* mysql_con)
5942+
static my_bool process_set_gtid_purged(MYSQL* mysql_con, my_bool flag)
59315943
{
5932-
MYSQL_RES *gtid_mode_res;
5933-
MYSQL_ROW gtid_mode_row;
5934-
char *gtid_mode_val= 0;
5944+
MYSQL_RES *gtid_mode_res;
5945+
MYSQL_ROW gtid_mode_row;
5946+
char *gtid_mode_val= 0;
5947+
static int gtid_mode= -1;
59355948
char buf[32], query[64];
59365949

59375950
if (opt_set_gtid_purged_mode == SET_GTID_PURGED_OFF)
59385951
return FALSE; /* nothing to be done */
59395952

59405953
/*
5941-
Check if the server has the knowledge of GTIDs(pre mysql-5.6)
5942-
or if the gtid_mode is ON or OFF.
5954+
Set gtid_mode, by fetching gtid_mode from server, if its not
5955+
yet populated. gtid_mode is set to -1 if gtid_mode is not yet
5956+
fetched from the server.
59435957
*/
5944-
my_snprintf(query, sizeof(query), "SHOW VARIABLES LIKE %s",
5945-
quote_for_like("gtid_mode", buf));
5958+
if (gtid_mode < 0)
5959+
{
5960+
/*
5961+
Check if the server has the knowledge of GTIDs(pre mysql-5.6)
5962+
or if the gtid_mode is ON or OFF.
5963+
*/
5964+
my_snprintf(query, sizeof(query), "SHOW VARIABLES LIKE %s",
5965+
quote_for_like("gtid_mode", buf));
59465966

5947-
if (mysql_query_with_error_report(mysql_con, &gtid_mode_res, query))
5948-
return TRUE;
5967+
if (mysql_query_with_error_report(mysql_con, &gtid_mode_res, query))
5968+
return TRUE;
59495969

5950-
gtid_mode_row = mysql_fetch_row(gtid_mode_res);
5970+
gtid_mode_row = mysql_fetch_row(gtid_mode_res);
59515971

5952-
/*
5953-
gtid_mode_row is NULL for pre 5.6 versions. For versions >= 5.6,
5954-
get the gtid_mode value from the second column.
5955-
*/
5956-
gtid_mode_val = gtid_mode_row ? (char*)gtid_mode_row[1] : NULL;
5972+
/*
5973+
gtid_mode_row is NULL for pre 5.6 versions. For versions >= 5.6,
5974+
get the gtid_mode value from the second column.
5975+
*/
5976+
gtid_mode_val = gtid_mode_row ? (char*)gtid_mode_row[1] : NULL;
5977+
gtid_mode= (gtid_mode_val && strcmp(gtid_mode_val, "OFF")) ? 1 : 0;
5978+
mysql_free_result(gtid_mode_res);
5979+
}
59575980

5958-
if (gtid_mode_val && strcmp(gtid_mode_val, "OFF"))
5981+
if (gtid_mode)
59595982
{
59605983
/*
59615984
For any gtid_mode !=OFF and irrespective of --set-gtid-purged
59625985
being AUTO or ON, add GTID_PURGED in the output.
59635986
*/
5964-
if (opt_databases || !opt_alldbs || !opt_dump_triggers
5965-
|| !opt_routines || !opt_events)
5987+
if (!flag)
5988+
set_session_binlog();
5989+
else
59665990
{
5967-
fprintf(stderr,"Warning: A partial dump from a server that has GTIDs will "
5968-
"by default include the GTIDs of all transactions, even "
5969-
"those that changed suppressed parts of the database. If "
5970-
"you don't want to restore GTIDs, pass "
5971-
"--set-gtid-purged=OFF. To make a complete dump, pass "
5972-
"--all-databases --triggers --routines --events. \n");
5973-
}
5991+
if (flag && (opt_databases || !opt_alldbs || !opt_dump_triggers
5992+
|| !opt_routines || !opt_events))
5993+
{
5994+
fprintf(stderr,"Warning: A partial dump from a server that has GTIDs will "
5995+
"by default include the GTIDs of all transactions, even "
5996+
"those that changed suppressed parts of the database. If "
5997+
"you don't want to restore GTIDs, pass "
5998+
"--set-gtid-purged=OFF. To make a complete dump, pass "
5999+
"--all-databases --triggers --routines --events. \n");
6000+
}
59746001

5975-
set_session_binlog(FALSE);
5976-
if (add_set_gtid_purged(mysql_con))
5977-
{
5978-
mysql_free_result(gtid_mode_res);
5979-
return TRUE;
6002+
if (add_set_gtid_purged(mysql_con))
6003+
return TRUE;
59806004
}
59816005
}
59826006
else /* gtid_mode is off */
59836007
{
5984-
if (opt_set_gtid_purged_mode == SET_GTID_PURGED_ON)
6008+
if (flag && opt_set_gtid_purged_mode == SET_GTID_PURGED_ON)
59856009
{
59866010
fprintf(stderr, "Error: Server has GTIDs disabled.\n");
5987-
mysql_free_result(gtid_mode_res);
59886011
return TRUE;
59896012
}
59906013
}
59916014

5992-
mysql_free_result(gtid_mode_res);
59936015
return FALSE;
59946016
}
59956017

@@ -6323,12 +6345,10 @@ int main(int argc, char **argv)
63236345
if (opt_slave_apply && add_stop_slave())
63246346
goto err;
63256347

6326-
6327-
/* Process opt_set_gtid_purged and add SET @@GLOBAL.GTID_PURGED if required. */
6328-
if (process_set_gtid_purged(mysql))
6348+
/* Process opt_set_gtid_purged and add SET disable binlog if required. */
6349+
if (process_set_gtid_purged(mysql, FALSE))
63296350
goto err;
63306351

6331-
63326352
if (opt_master_data && do_show_master_status(mysql))
63336353
goto err;
63346354
if (opt_slave_data && do_show_slave_status(mysql))
@@ -6381,11 +6401,9 @@ int main(int argc, char **argv)
63816401
if (opt_slave_data && do_start_slave_sql(mysql))
63826402
goto err;
63836403

6384-
/*
6385-
if --set-gtid-purged, restore binlog at the end of the session
6386-
if required.
6387-
*/
6388-
set_session_binlog(TRUE);
6404+
/* Process opt_set_gtid_purged and add SET @@GLOBAL.GTID_PURGED if required. */
6405+
if (process_set_gtid_purged(mysql, TRUE))
6406+
goto err;
63896407

63906408
/* add 'START SLAVE' to end of dump */
63916409
if (opt_slave_apply && add_slave_statements())
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#
2+
# Bug#32843447: GTID_PURGED IS GETTING EMPTY AND GTID_EXECUTED IS
3+
# GETTING UPDATED AFTER RESTARTING MYSQL
4+
#
5+
CREATE TABLE t (a INT PRIMARY KEY, b INT);
6+
INSERT INTO t VALUES (1, 1);
7+
INSERT INTO t VALUES (2, 1);
8+
INSERT INTO t VALUES (3, 1);
9+
INSERT INTO t VALUES (4, 1);
10+
INSERT INTO t VALUES (5, 1);
11+
INSERT INTO t VALUES (6, 1);
12+
FLUSH LOGS;
13+
# BEFORE RESET
14+
include/assert.inc [Committed gtids :- MASTER_UUID:1-7]
15+
include/assert.inc [No purged gtids]
16+
SELECT * FROM mysql.gtid_executed;
17+
source_uuid interval_start interval_end
18+
MASTER_UUID 1 7
19+
# MYSQLDUMP SKIPING THE DROP DATABASE FOR MYSQL SCHEMA USING THE OPTION --skip-mysql-schema
20+
Pattern "DROP DATABASE IF EXISTS `mysql`" not found
21+
# MYSQLDUMP WITHOUT SKIPING THE DROP DATABASE FOR MYSQL SCHEMA
22+
Pattern "DROP DATABASE IF EXISTS `mysql`" found
23+
# RESET
24+
RESET MASTER;
25+
# AFTER RESET
26+
include/assert.inc [No committed gtids after RESET]
27+
include/assert.inc [No purged gtids after RESET]
28+
SELECT * FROM mysql.gtid_executed;
29+
source_uuid interval_start interval_end
30+
# DUMP RESTORE WITH THE DUMP FILE HAVING DROP DATABASE ON MYSQL SCHEMA.
31+
# AFTER RESTORE
32+
include/assert.inc [Committed gtids after restore :- MASTER_UUID:1-7]
33+
include/assert.inc [Purged gtids after restore :- MASTER_UUID:1-7]
34+
SELECT * FROM mysql.gtid_executed;
35+
source_uuid interval_start interval_end
36+
MASTER_UUID 1 7
37+
# INSERT
38+
INSERT INTO t VALUES (7, 1);
39+
INSERT INTO t VALUES (8, 1);
40+
INSERT INTO t VALUES (9, 1);
41+
# AFTER INSERTING
42+
include/assert.inc [Committed gtids after inserting :- MASTER_UUID:1-10]
43+
include/assert.inc [Purged gtids after inserting :- MASTER_UUID:1-7]
44+
SELECT * FROM mysql.gtid_executed;
45+
source_uuid interval_start interval_end
46+
MASTER_UUID 1 7
47+
# RESTART
48+
# restart
49+
# AFTER RESTART
50+
include/assert.inc [Committed gtids after restart :- MASTER_UUID:1-10]
51+
include/assert.inc [Purged gtids after restart :- MASTER_UUID:1-7]
52+
SELECT * FROM mysql.gtid_executed;
53+
source_uuid interval_start interval_end
54+
MASTER_UUID 1 7
55+
MASTER_UUID 8 10
56+
include/assert.inc [GTID_EXECUTED is correct after the restart]
57+
CALL mtr.add_suppression(".*InnoDB: Table `mysql`.`innodb_table_stats` not found.*");
58+
SHOW CREATE TABLE `mysql`.`innodb_table_stats`;
59+
Table Create Table
60+
innodb_table_stats CREATE TABLE `innodb_table_stats` (
61+
`database_name` varchar(64) COLLATE utf8_bin NOT NULL,
62+
`table_name` varchar(199) COLLATE utf8_bin NOT NULL,
63+
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
64+
`n_rows` bigint(20) unsigned NOT NULL,
65+
`clustered_index_size` bigint(20) unsigned NOT NULL,
66+
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
67+
PRIMARY KEY (`database_name`,`table_name`)
68+
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0
69+
#CLEANUP
70+
DROP TABLE t;
71+
RESET MASTER;
72+
# restart
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--log-slave-updates --enforce-gtid-consistency --gtid-mode=ON --log-bin

0 commit comments

Comments
 (0)