From: Andrei <andrei.elkin@mariadb.com> When mysqldump run on mysql system database it generates inserts sql commands into mysql.gtid_slave_pos. After running the backup script those inserts did not produce an expected gtid state on slave. In particular the maximum of mysql.gtid_slave_pos.sub_id did not make into rpl_global_gtid_slave_state.last_sub_id a memory object that the table persists. And that was regardless of whether --gtid option in or out. Later when the backup recipient server starts as slave in *non-gtid* mode this desychronization may lead to a duplicate key error. This effect is corrected for --gtid mode mysqldump/mariadb-dump only as the following. The fixes ensure the insert block of the dump script is followed with a "summing-up" SET @global.gtid_slave_pos assignment. To address MDEV-34615 the assignment makes sure a possibly pre-existing gtid state (e.g reflecting another domain) remains as a sub-state upon the dump installation. A new test provides a how-to-reproduce mode as well. For the implemenation part, note a deferred print-out of SET-gtid_slave_pos and associated comments is prefered over relocating of the entire blocks if (opt_master,slave_data && do_show_master,slave_status) ... because of compatiblity concern. Namely an error inside do_show_*() is handled in the new code the same way, as early, as before. --- client/mysqldump.c | 53 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 1c9d92d6e09..0be890e2528 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -5985,8 +5985,13 @@ static int dump_selected_tables(char *db, char **table_names, int tables) } /* dump_selected_tables */ +const char fmt_gtid_pos[] = "%sSET GLOBAL gtid_slave_pos=concat('%s', " + "if(length(@@global.gtid_slave_pos) = 0, '', ','), " + "@@global.gtid_slave_pos);\n"; + static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos, - int have_mariadb_gtid, int use_gtid) + int have_mariadb_gtid, int use_gtid, + char *set_gtid_pos) { MYSQL_ROW row; MYSQL_RES *UNINIT_VAR(master); @@ -6054,8 +6059,11 @@ static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos, fprintf(md_result_file, "%sCHANGE MASTER TO MASTER_USE_GTID=slave_pos;\n", comment_prefix); - fprintf(md_result_file, - "%sSET GLOBAL gtid_slave_pos='%s';\n", + /* + Defer print of SET gtid_slave_pos until after its placeholder + table is guaranteed to have been dumped. + */ + sprintf(set_gtid_pos, fmt_gtid_pos, (!use_gtid ? "-- " : comment_prefix), gtid_pos); } @@ -6140,8 +6148,8 @@ static int add_slave_statements(void) return(0); } -static int do_show_slave_status(MYSQL *mysql_con, int use_gtid, - int have_mariadb_gtid) +static int do_show_slave_status(MYSQL *mysql_con, int have_mariadb_gtid, + int use_gtid, char* set_gtid_pos) { MYSQL_RES *UNINIT_VAR(slave); MYSQL_ROW row; @@ -6181,10 +6189,8 @@ static int do_show_slave_status(MYSQL *mysql_con, int use_gtid, mysql_free_result(slave); return 1; } - print_comment(md_result_file, 0, - "-- GTID position to start replication:\n"); - fprintf(md_result_file, "%sSET GLOBAL gtid_slave_pos='%s';\n", - gtid_comment_prefix, gtid_pos); + /* similarly to do_show_master_status */ + sprintf(set_gtid_pos, fmt_gtid_pos, gtid_comment_prefix, gtid_pos); } if (use_gtid) print_comment(md_result_file, 0, @@ -6905,6 +6911,19 @@ static void dynstr_realloc_checked(DYNAMIC_STRING *str, ulong additional_size) die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG); } +/** + Print earlier prepared SET to @@global.gtid_slave_pos. + + @param set_gtid_pos[in] formatted sql set statement +**/ +static void do_print_set_gtid_slave_pos(const char *set_gtid_pos) +{ + DBUG_ASSERT(opt_master_data || opt_slave_data); + if (opt_slave_data) + print_comment(md_result_file, 0, + "-- GTID position to start replication:\n"); + fprintf(md_result_file, "%s", set_gtid_pos); +} int main(int argc, char **argv) { @@ -6913,6 +6932,11 @@ int main(int argc, char **argv) int exit_code; int consistent_binlog_pos= 0; int have_mariadb_gtid= 0; + /* + to hold SET @@global.gtid_slave_pos which is deferred to print + until the function epilogue. + */ + char set_gtid_pos[3 + sizeof(fmt_gtid_pos) + MAX_GTID_LENGTH]= {0}; MY_INIT(argv[0]); sf_leaking_memory=1; /* don't report memory leaks on early exits */ @@ -7016,10 +7040,12 @@ int main(int argc, char **argv) goto err; if (opt_master_data && do_show_master_status(mysql, consistent_binlog_pos, - have_mariadb_gtid, opt_use_gtid)) + have_mariadb_gtid, + opt_use_gtid, set_gtid_pos)) goto err; - if (opt_slave_data && do_show_slave_status(mysql, opt_use_gtid, - have_mariadb_gtid)) + if (opt_slave_data && do_show_slave_status(mysql, + have_mariadb_gtid, + opt_use_gtid, set_gtid_pos)) goto err; if (opt_single_transaction && do_unlock_tables(mysql)) /* unlock but no commit! */ goto err; @@ -7087,6 +7113,9 @@ int main(int argc, char **argv) if (opt_system & OPT_SYSTEM_TIMEZONES) dump_all_timezones(); + if ((opt_master_data || opt_slave_data) && set_gtid_pos[0]) + do_print_set_gtid_slave_pos(set_gtid_pos); + /* add 'START SLAVE' to end of dump */ if (opt_slave_apply && add_slave_statements()) goto err; -- 2.39.2