MDEV-6391: GTID binlog state not recovered if mariadb-bin.state is removed
When the server starts up, check if the master-bin.state file was lost. If it was, recover its contents by scanning the last binlog file, thus avoiding running with a corrupt binlog state.
This commit is contained in:
parent
ec4ff9a2e7
commit
aa845d123c
@ -267,5 +267,55 @@ a
|
|||||||
24
|
24
|
||||||
26
|
26
|
||||||
27
|
27
|
||||||
|
*** MDEV-6391: GTID binlog state not recovered if mariadb-bin.state is removed ***
|
||||||
|
include/stop_slave.inc
|
||||||
|
INSERT INTO t1 VALUES (30);
|
||||||
|
SET @old_server_id= @@server_id;
|
||||||
|
SET @old_domain_id= @@gtid_domain_id;
|
||||||
|
SET SESSION server_id= 10;
|
||||||
|
INSERT INTO t1 VALUES (31);
|
||||||
|
INSERT INTO t1 VALUES (32);
|
||||||
|
SET SESSION gtid_domain_id= 1;
|
||||||
|
SET SESSION server_id=11;
|
||||||
|
INSERT INTO t1 VALUES (33);
|
||||||
|
SET SESSION gtid_domain_id= 2;
|
||||||
|
INSERT INTO t1 VALUES (34);
|
||||||
|
SET SESSION server_id= 10;
|
||||||
|
INSERT INTO t1 VALUES (35);
|
||||||
|
INSERT INTO t1 VALUES (36);
|
||||||
|
SET SESSION gtid_domain_id= 0;
|
||||||
|
SET SESSION server_id= 12;
|
||||||
|
INSERT INTO t1 VALUES (37);
|
||||||
|
SET SESSION gtid_domain_id= @old_domain_id;
|
||||||
|
SET SESSION server_id= @old_server_id;
|
||||||
|
INSERT INTO t1 VALUES (38);
|
||||||
|
INSERT INTO t1 VALUES (39);
|
||||||
|
SELECT * FROM t1 WHERE a >= 30 ORDER BY a;
|
||||||
|
a
|
||||||
|
30
|
||||||
|
31
|
||||||
|
32
|
||||||
|
33
|
||||||
|
34
|
||||||
|
35
|
||||||
|
36
|
||||||
|
37
|
||||||
|
38
|
||||||
|
39
|
||||||
|
include/save_master_gtid.inc
|
||||||
|
include/start_slave.inc
|
||||||
|
include/sync_with_master_gtid.inc
|
||||||
|
SELECT * FROM t1 WHERE a >= 30 ORDER BY a;
|
||||||
|
a
|
||||||
|
30
|
||||||
|
31
|
||||||
|
32
|
||||||
|
33
|
||||||
|
34
|
||||||
|
35
|
||||||
|
36
|
||||||
|
37
|
||||||
|
38
|
||||||
|
39
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
include/rpl_end.inc
|
include/rpl_end.inc
|
||||||
|
@ -587,6 +587,77 @@ eval SELECT IF(INSTR(@@gtid_current_pos, '$saved_gtid'), "Current pos ok", CONCA
|
|||||||
SELECT * from t1 WHERE a > 10 ORDER BY a;
|
SELECT * from t1 WHERE a > 10 ORDER BY a;
|
||||||
|
|
||||||
|
|
||||||
|
--echo *** MDEV-6391: GTID binlog state not recovered if mariadb-bin.state is removed ***
|
||||||
|
|
||||||
|
--connection server_2
|
||||||
|
--source include/stop_slave.inc
|
||||||
|
|
||||||
|
# Do some misc. transactions, stop the master, drop the master-bin.state file.
|
||||||
|
# Start the master back up, check that binlog state is correct.
|
||||||
|
|
||||||
|
--connection server_1
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES (30);
|
||||||
|
SET @old_server_id= @@server_id;
|
||||||
|
SET @old_domain_id= @@gtid_domain_id;
|
||||||
|
|
||||||
|
SET SESSION server_id= 10;
|
||||||
|
INSERT INTO t1 VALUES (31);
|
||||||
|
INSERT INTO t1 VALUES (32);
|
||||||
|
SET SESSION gtid_domain_id= 1;
|
||||||
|
SET SESSION server_id=11;
|
||||||
|
INSERT INTO t1 VALUES (33);
|
||||||
|
SET SESSION gtid_domain_id= 2;
|
||||||
|
INSERT INTO t1 VALUES (34);
|
||||||
|
SET SESSION server_id= 10;
|
||||||
|
INSERT INTO t1 VALUES (35);
|
||||||
|
INSERT INTO t1 VALUES (36);
|
||||||
|
SET SESSION gtid_domain_id= 0;
|
||||||
|
SET SESSION server_id= 12;
|
||||||
|
INSERT INTO t1 VALUES (37);
|
||||||
|
SET SESSION gtid_domain_id= @old_domain_id;
|
||||||
|
SET SESSION server_id= @old_server_id;
|
||||||
|
INSERT INTO t1 VALUES (38);
|
||||||
|
INSERT INTO t1 VALUES (39);
|
||||||
|
SELECT * FROM t1 WHERE a >= 30 ORDER BY a;
|
||||||
|
--source include/save_master_gtid.inc
|
||||||
|
|
||||||
|
--let OLD_STATE= `SELECT @@gtid_binlog_state`
|
||||||
|
|
||||||
|
--let $datadir= `SELECT @@datadir`
|
||||||
|
|
||||||
|
--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
||||||
|
wait
|
||||||
|
EOF
|
||||||
|
shutdown_server 10;
|
||||||
|
--source include/wait_until_disconnected.inc
|
||||||
|
|
||||||
|
--remove_file $datadir/master-bin.state
|
||||||
|
|
||||||
|
--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
||||||
|
restart
|
||||||
|
EOF
|
||||||
|
--enable_reconnect
|
||||||
|
--source include/wait_until_connected_again.inc
|
||||||
|
|
||||||
|
--let NEW_STATE= `SELECT @@gtid_binlog_state`
|
||||||
|
|
||||||
|
--perl
|
||||||
|
my $old= $ENV{'OLD_STATE'};
|
||||||
|
my $new= $ENV{'NEW_STATE'};
|
||||||
|
# Make them order-independent, for easy comparison.
|
||||||
|
$old= join(",", sort(split(",", $old)));
|
||||||
|
$new= join(",", sort(split(",", $new)));
|
||||||
|
die "ERROR: new binlog state '$new' differs from old '$old'\n"
|
||||||
|
unless $old eq $new;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
--connection server_2
|
||||||
|
--source include/start_slave.inc
|
||||||
|
--source include/sync_with_master_gtid.inc
|
||||||
|
SELECT * FROM t1 WHERE a >= 30 ORDER BY a;
|
||||||
|
|
||||||
|
|
||||||
--connection server_1
|
--connection server_1
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
61
sql/log.cc
61
sql/log.cc
@ -5653,6 +5653,14 @@ end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialize the binlog state from the master-bin.state file, at server startup.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
0 for success.
|
||||||
|
2 for when .state file did not exist.
|
||||||
|
1 for other error.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
MYSQL_BIN_LOG::read_state_from_file()
|
MYSQL_BIN_LOG::read_state_from_file()
|
||||||
{
|
{
|
||||||
@ -5680,7 +5688,7 @@ MYSQL_BIN_LOG::read_state_from_file()
|
|||||||
with GTID enabled. So initialize to empty state.
|
with GTID enabled. So initialize to empty state.
|
||||||
*/
|
*/
|
||||||
rpl_global_gtid_binlog_state.reset();
|
rpl_global_gtid_binlog_state.reset();
|
||||||
err= 0;
|
err= 2;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9444,7 +9452,17 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
|
|||||||
if (error != LOG_INFO_EOF)
|
if (error != LOG_INFO_EOF)
|
||||||
sql_print_error("find_log_pos() failed (error: %d)", error);
|
sql_print_error("find_log_pos() failed (error: %d)", error);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
error= read_state_from_file();
|
error= read_state_from_file();
|
||||||
|
if (error == 2)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
No binlog files and no binlog state is not an error (eg. just initial
|
||||||
|
server start after fresh installation).
|
||||||
|
*/
|
||||||
|
error= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9470,15 +9488,42 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
|
|||||||
|
|
||||||
if ((ev= Log_event::read_log_event(&log, 0, &fdle,
|
if ((ev= Log_event::read_log_event(&log, 0, &fdle,
|
||||||
opt_master_verify_checksum)) &&
|
opt_master_verify_checksum)) &&
|
||||||
ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
|
ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
|
||||||
ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
|
|
||||||
{
|
{
|
||||||
sql_print_information("Recovering after a crash using %s", opt_name);
|
if (ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
|
||||||
error= recover(&log_info, log_name, &log,
|
{
|
||||||
(Format_description_log_event *)ev, do_xa_recovery);
|
sql_print_information("Recovering after a crash using %s", opt_name);
|
||||||
|
error= recover(&log_info, log_name, &log,
|
||||||
|
(Format_description_log_event *)ev, do_xa_recovery);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error= read_state_from_file();
|
||||||
|
if (error == 2)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The binlog exists, but the .state file is missing. This is normal if
|
||||||
|
this is the first master start after a major upgrade to 10.0 (with
|
||||||
|
GTID support).
|
||||||
|
|
||||||
|
However, it could also be that the .state file was lost somehow, and
|
||||||
|
in this case it could be a serious issue, as we would set the wrong
|
||||||
|
binlog state in the next binlog file to be created, and GTID
|
||||||
|
processing would be corrupted. A common way would be copying files
|
||||||
|
from an old server to a new one and forgetting the .state file.
|
||||||
|
|
||||||
|
So in this case, we want to try to recover the binlog state by
|
||||||
|
scanning the last binlog file (but we do not need any XA recovery).
|
||||||
|
|
||||||
|
ToDo: We could avoid one scan at first start after major upgrade, by
|
||||||
|
detecting that there is no GTID_LIST event at the start of the
|
||||||
|
binlog file, and stopping the scan in that case.
|
||||||
|
*/
|
||||||
|
error= recover(&log_info, log_name, &log,
|
||||||
|
(Format_description_log_event *)ev, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
error= read_state_from_file();
|
|
||||||
|
|
||||||
delete ev;
|
delete ev;
|
||||||
end_io_cache(&log);
|
end_io_cache(&log);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user