diff --git a/mysql-test/suite/rpl/r/rpl_gtid_strict.result b/mysql-test/suite/rpl/r/rpl_gtid_strict.result --- a/mysql-test/suite/rpl/r/rpl_gtid_strict.result +++ b/mysql-test/suite/rpl/r/rpl_gtid_strict.result @@ -3,10 +3,8 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; SET sql_log_bin= 0; call mtr.add_suppression("Error writing file .*errno: 1949"); SET sql_log_bin= 1; -SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode; SET GLOBAL gtid_strict_mode= 1; include/stop_slave.inc -SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode; SET GLOBAL gtid_strict_mode=1; CHANGE MASTER TO master_use_gtid=slave_pos; include/start_slave.inc @@ -156,8 +154,10 @@ a *** Check slave requests starting from a hole on the master. *** include/stop_slave.inc INSERT INTO t1 VALUES (10); +SET GLOBAL gtid_strict_mode = 0; SET gtid_seq_no= 100; INSERT INTO t1 VALUES (11); +SET GLOBAL gtid_strict_mode = 1; INSERT INTO t1 VALUES (12); SET GLOBAL gtid_slave_pos= "0-1-50"; START SLAVE; @@ -176,7 +176,106 @@ a 11 12 SET GLOBAL gtid_strict_mode= 1; -DROP TABLE t1, t2, t3; -SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode; -SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode; +*** Check that gap in seq_no without binlogs doesn't allow to replicate. *** +SET sql_log_bin= 0; +call mtr.add_suppression("slave state is too old"); +SET sql_log_bin= 1; +# On the master +SELECT @@global.gtid_current_pos; +@@global.gtid_current_pos +0-1-101 +include/stop_slave.inc +# Restarting server_1 without binlogs +# Making a gap and restarting slave +SET GLOBAL gtid_slave_pos = '0-1-105'; +INSERT INTO t1 VALUES (13); +START SLAVE; +include/wait_for_slave_io_error.inc [errno=1236] +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +include/start_slave.inc +SET GLOBAL gtid_strict_mode= 1; +# The same test but with starting master GTID from different server_id +include/stop_slave.inc +# Restarting server_1 without binlogs +# Making a gap and restarting slave +SET GLOBAL gtid_slave_pos = '0-5-110'; +INSERT INTO t1 VALUES (14); +START SLAVE; +include/wait_for_slave_io_error.inc [errno=1236] +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +include/start_slave.inc +SET GLOBAL gtid_strict_mode= 1; +*** Check processing of alternate futures +SET sql_log_bin= 0; +call mtr.add_suppression("Slave appears to be from an alternate future"); +SET sql_log_bin= 1; +# First check alt future with binlogs +include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID = current_pos; +# On the slave (when slave threads are stopped) +INSERT INTO t1 VALUES (15); +SELECT @@global.gtid_current_pos; +@@global.gtid_current_pos +0-2-112 +# On the master +INSERT INTO t1 VALUES (16); +INSERT INTO t1 VALUES (17); +SELECT @@global.gtid_current_pos; +@@global.gtid_current_pos +0-1-113 +# On the slave +START SLAVE; +include/wait_for_slave_io_error.inc [errno=1236] +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +include/start_slave.inc +SET GLOBAL gtid_strict_mode= 1; +# Check alt future when divergence is in last event in binlog file +include/stop_slave.inc +# On the slave (when slave threads are stopped) +CHANGE MASTER TO MASTER_USE_GTID = current_pos; +INSERT INTO t1 VALUES (19); +SELECT @@global.gtid_current_pos; +@@global.gtid_current_pos +0-2-114 +# On the master +INSERT INTO t1 VALUES (20); +FLUSH LOGS; +INSERT INTO t1 VALUES (21); +SELECT @@global.gtid_current_pos; +@@global.gtid_current_pos +0-1-115 +# On the slave +START SLAVE; +include/wait_for_slave_io_error.inc [errno=1236] +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +include/start_slave.inc +SET GLOBAL gtid_strict_mode= 1; +# Check alt future without binlogs +include/stop_slave.inc +# On the slave (when slave threads are stopped) +CHANGE MASTER TO MASTER_USE_GTID = current_pos; +INSERT INTO t1 VALUES (22); +SELECT @@global.gtid_current_pos; +@@global.gtid_current_pos +0-2-116 +# Restarting server_1 without binlogs +# Creating alt future and restarting slave +SET GLOBAL gtid_slave_pos = '0-1-116'; +START SLAVE; +include/wait_for_slave_io_error.inc [errno=1236] +# Recovering from replication error +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +SET GLOBAL gtid_slave_pos = '0-1-116'; +include/start_slave.inc +INSERT INTO t1 VALUES (23); +include/stop_slave.inc +include/start_slave.inc include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_gtid_strict.test b/mysql-test/suite/rpl/t/rpl_gtid_strict.test --- a/mysql-test/suite/rpl/t/rpl_gtid_strict.test +++ b/mysql-test/suite/rpl/t/rpl_gtid_strict.test @@ -11,11 +11,11 @@ call mtr.add_suppression("Error writing file .*errno: 1949"); SET sql_log_bin= 1; -SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode; +let $old_gtid_strict_mode_1= `SELECT @@GLOBAL.gtid_strict_mode`; SET GLOBAL gtid_strict_mode= 1; --connection server_2 --source include/stop_slave.inc -SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode; +let $old_gtid_strict_mode_2= `SELECT @@GLOBAL.gtid_strict_mode`; SET GLOBAL gtid_strict_mode=1; CHANGE MASTER TO master_use_gtid=slave_pos; --source include/start_slave.inc @@ -149,8 +149,10 @@ SELECT * FROM t2 ORDER BY a; --connection server_1 INSERT INTO t1 VALUES (10); +SET GLOBAL gtid_strict_mode = 0; SET gtid_seq_no= 100; INSERT INTO t1 VALUES (11); +SET GLOBAL gtid_strict_mode = 1; INSERT INTO t1 VALUES (12); --save_master_pos @@ -168,11 +170,193 @@ SELECT * FROM t1 ORDER BY a; SET GLOBAL gtid_strict_mode= 1; +--echo *** Check that gap in seq_no without binlogs doesn't allow to replicate. *** +SET sql_log_bin= 0; +call mtr.add_suppression("slave state is too old"); +SET sql_log_bin= 1; +--connection server_1 +--echo # On the master +SELECT @@global.gtid_current_pos; +--connection server_2 +--source include/stop_slave.inc + +--echo # Restarting server_1 without binlogs +--connection server_1 +--let $MYSQLD_DATADIR= `select @@datadir` +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +--exec rm $MYSQLD_DATADIR/master-bin.* + +--enable_reconnect +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--source include/wait_until_connected_again.inc + +--echo # Making a gap and restarting slave +--connection server_1 +SET GLOBAL gtid_slave_pos = '0-1-105'; +INSERT INTO t1 VALUES (13); +--save_master_pos + +--connection server_2 +START SLAVE; +--let $slave_io_errno=1236 +--source include/wait_for_slave_io_error.inc +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +--source include/start_slave.inc +--sync_with_master +SET GLOBAL gtid_strict_mode= 1; + + +--echo # The same test but with starting master GTID from different server_id +--connection server_2 +--source include/stop_slave.inc + +--echo # Restarting server_1 without binlogs +--connection server_1 +--let $MYSQLD_DATADIR= `select @@datadir` +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +--exec rm $MYSQLD_DATADIR/master-bin.* + +--enable_reconnect +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--source include/wait_until_connected_again.inc + +--echo # Making a gap and restarting slave +--connection server_1 +SET GLOBAL gtid_slave_pos = '0-5-110'; +INSERT INTO t1 VALUES (14); +--save_master_pos + +--connection server_2 +START SLAVE; +--let $slave_io_errno=1236 +--source include/wait_for_slave_io_error.inc +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +--source include/start_slave.inc +--sync_with_master +SET GLOBAL gtid_strict_mode= 1; + + +--echo *** Check processing of alternate futures +SET sql_log_bin= 0; +call mtr.add_suppression("Slave appears to be from an alternate future"); +SET sql_log_bin= 1; +--echo # First check alt future with binlogs +--connection server_2 +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID = current_pos; +--echo # On the slave (when slave threads are stopped) +INSERT INTO t1 VALUES (15); +SELECT @@global.gtid_current_pos; +--connection server_1 +--echo # On the master +INSERT INTO t1 VALUES (16); +INSERT INTO t1 VALUES (17); +SELECT @@global.gtid_current_pos; +--save_master_pos + +--connection server_2 +--echo # On the slave +START SLAVE; +--let $slave_io_errno=1236 +--source include/wait_for_slave_io_error.inc +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +--source include/start_slave.inc +--sync_with_master +SET GLOBAL gtid_strict_mode= 1; + + +--echo # Check alt future when divergence is in last event in binlog file +--connection server_2 +--source include/stop_slave.inc +--echo # On the slave (when slave threads are stopped) +CHANGE MASTER TO MASTER_USE_GTID = current_pos; +INSERT INTO t1 VALUES (19); +SELECT @@global.gtid_current_pos; +--connection server_1 +--echo # On the master +INSERT INTO t1 VALUES (20); +FLUSH LOGS; +INSERT INTO t1 VALUES (21); +SELECT @@global.gtid_current_pos; +--save_master_pos + +--connection server_2 +--echo # On the slave +START SLAVE; +--let $slave_io_errno=1236 +--source include/wait_for_slave_io_error.inc +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +--source include/start_slave.inc +--sync_with_master +SET GLOBAL gtid_strict_mode= 1; + + +--echo # Check alt future without binlogs +--connection server_2 +--source include/stop_slave.inc +--echo # On the slave (when slave threads are stopped) +CHANGE MASTER TO MASTER_USE_GTID = current_pos; +INSERT INTO t1 VALUES (22); +SELECT @@global.gtid_current_pos; + +--echo # Restarting server_1 without binlogs +--connection server_1 +--let $MYSQLD_DATADIR= `select @@datadir` +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +--exec rm $MYSQLD_DATADIR/master-bin.* + +--enable_reconnect +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--source include/wait_until_connected_again.inc + +--echo # Creating alt future and restarting slave +--connection server_1 +SET GLOBAL gtid_slave_pos = '0-1-116'; +--connection server_2 +START SLAVE; +--let $slave_io_errno=1236 +--source include/wait_for_slave_io_error.inc +--echo # Recovering from replication error +STOP SLAVE SQL_THREAD; +SET GLOBAL gtid_strict_mode= 0; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +SET GLOBAL gtid_slave_pos = '0-1-116'; +--source include/start_slave.inc +--connection server_1 +INSERT INTO t1 VALUES (23); +--save_master_pos +--connection server_2 +--sync_with_master + + # Clean up. +--disable_query_log --connection server_1 DROP TABLE t1, t2, t3; -SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode; +eval SET GLOBAL gtid_strict_mode= $old_gtid_strict_mode_1; --connection server_2 -SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode; +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +eval SET GLOBAL gtid_strict_mode= $old_gtid_strict_mode_2; +--source include/start_slave.inc +--enable_query_log +--connection default +--enable_reconnect +--source include/wait_until_connected_again.inc --source include/rpl_end.inc diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -993,7 +993,7 @@ rpl_binlog_state::check_strict_sequence(uint32 domain_id, uint32 server_id, if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)) && - elem->last_gtid && elem->last_gtid->seq_no >= seq_no) + elem->last_gtid && elem->last_gtid->seq_no + 1 != seq_no) { my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), domain_id, server_id, seq_no, elem->last_gtid->domain_id, elem->last_gtid->server_id, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6853,3 +6853,5 @@ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a transaction" ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a stored function or trigger" +ER_SLAVE_IS_FROM_ALTERNATE_FUTURE + eng "The slave provided a replication position with GTID %u-%u-%llu. The server id (%u) in this GTID is different from the server id (%d) in the GTID with the same domain id and sequence number on the master. Slave appears to be from an alternate future." diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -838,9 +838,12 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) to start at the very first GTID in domain D. */ static bool -contains_all_slave_gtid(slave_connection_state *st, Gtid_list_log_event *glev) +contains_all_slave_gtid(slave_connection_state *st, + Gtid_list_log_event *glev, + bool slave_gtid_strict_mode) { uint32 i; + bool has_greater_seq_no= false; for (i= 0; i < glev->count; ++i) { @@ -854,8 +857,16 @@ contains_all_slave_gtid(slave_connection_state *st, Gtid_list_log_event *glev) */ return false; } - if (gtid->server_id == glev->list[i].server_id && - gtid->seq_no <= glev->list[i].seq_no) + if (slave_gtid_strict_mode) + { + if (gtid->seq_no < glev->list[i].seq_no) + { + has_greater_seq_no= true; + break; + } + } + else if (gtid->server_id == glev->list[i].server_id && + gtid->seq_no <= glev->list[i].seq_no) { /* The slave needs to start after gtid, but it is contained in an earlier @@ -880,7 +891,7 @@ contains_all_slave_gtid(slave_connection_state *st, Gtid_list_log_event *glev) } } - return true; + return !slave_gtid_strict_mode || !has_greater_seq_no; } @@ -893,9 +904,10 @@ contains_all_slave_gtid(slave_connection_state *st, Gtid_list_log_event *glev) static int check_slave_start_position(THD *thd, slave_connection_state *st, - const char **errormsg, rpl_gtid *error_gtid, + const char **errormsg, + rpl_gtid *error_gtid, rpl_gtid *good_gtid, slave_connection_state *until_gtid_state, - HASH *fake_gtid_hash) + HASH *fake_gtid_hash, bool slave_gtid_strict_mode) { uint32 i; int err; @@ -927,8 +939,10 @@ check_slave_start_position(THD *thd, slave_connection_state *st, slave_state_loaded= true; } - if (!rpl_global_gtid_slave_state.domain_to_gtid(slave_gtid->domain_id, - &master_replication_gtid) || + bool master_repl_gtid_found = + rpl_global_gtid_slave_state.domain_to_gtid(slave_gtid->domain_id, + &master_replication_gtid); + if (!master_repl_gtid_found || slave_gtid->server_id != master_replication_gtid.server_id || slave_gtid->seq_no != master_replication_gtid.seq_no) { @@ -939,9 +953,32 @@ check_slave_start_position(THD *thd, slave_connection_state *st, &domain_gtid)) { /* - We do not have anything in this domain, neither in the binlog nor - in the slave state. So we are probably one master in a multi-master - setup, and this domain is served by a different master. + We do not have anything in the binlog for this domain, and the slave + state either doesn't have anything or has GTID from different server. + In the former case we are probably one master in a multi-master + setup, and this domain is served by a different master, in the latter + we may have restarted with all binlogs deleted. + */ + if (!slave_gtid_strict_mode || !master_repl_gtid_found) + continue; + if (slave_gtid->seq_no == master_replication_gtid.seq_no) + { + *errormsg= "Requested slave GTID state have different server id " + "than the one in gtid_slave_pos"; + *error_gtid= *slave_gtid; + *good_gtid= master_replication_gtid; + err= ER_SLAVE_IS_FROM_ALTERNATE_FUTURE; + goto end; + } + } + else if (slave_gtid_strict_mode && + slave_gtid->seq_no <= domain_gtid.seq_no) + { + /* + Slave requested GTID which is not present as is in the binlog. This + can mean either the slave is from alternate future or it requests + GTID that is before the start of binlog. We'll distinguish these two + situations later. */ continue; } @@ -1095,21 +1132,25 @@ end: that might turn up if that domain becomes active again, vainly looking for the requested GTID that was already purged. */ -static const char * -gtid_find_binlog_file(slave_connection_state *state, char *out_name, - slave_connection_state *until_gtid_state) +static int +gtid_find_binlog_file(slave_connection_state *state, + const char **errormsg, rpl_gtid *error_gtid, + rpl_gtid *good_gtid, char *out_name, + slave_connection_state *until_gtid_state, + bool slave_gtid_strict_mode) { MEM_ROOT memroot; binlog_file_entry *list; Gtid_list_log_event *glev= NULL; - const char *errormsg= NULL; + int error= 0; char buf[FN_REFLEN]; init_alloc_root(&memroot, 10*(FN_REFLEN+sizeof(binlog_file_entry)), 0, MYF(MY_THREAD_SPECIFIC)); if (!(list= get_binlog_list(&memroot))) { - errormsg= "Out of memory while looking for GTID position in binlog"; + *errormsg= "Out of memory while looking for GTID position in binlog"; + error= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto end; } @@ -1134,20 +1175,27 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name, */ if (normalize_binlog_name(buf, list->name, false)) { - errormsg= "Failed to determine binlog file name while looking for " + *errormsg= "Failed to determine binlog file name while looking for " "GTID position in binlog"; + error= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto end; } bzero((char*) &cache, sizeof(cache)); - if ((file= open_binlog(&cache, buf, &errormsg)) == (File)-1) + if ((file= open_binlog(&cache, buf, errormsg)) == (File)-1) + { + error= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto end; - errormsg= get_gtid_list_event(&cache, &glev); + } + *errormsg= get_gtid_list_event(&cache, &glev); end_io_cache(&cache); mysql_file_close(file, MYF(MY_WME)); - if (errormsg) + if (*errormsg) + { + error= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto end; + } - if (!glev || contains_all_slave_gtid(state, glev)) + if (!glev || contains_all_slave_gtid(state, glev, slave_gtid_strict_mode)) { strmake(out_name, buf, FN_REFLEN); @@ -1191,6 +1239,16 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name, */ state->remove(gtid); } + else if (slave_gtid_strict_mode && + gtid->seq_no == glev->list[i].seq_no) + { + *errormsg= "Requested slave GTID state have different server id " + "than the one in binlog"; + *error_gtid= *gtid; + *good_gtid= glev->list[i]; + error= ER_SLAVE_IS_FROM_ALTERNATE_FUTURE; + goto end; + } if (until_gtid_state && (gtid= until_gtid_state->find(glev->list[i].domain_id)) && @@ -1214,16 +1272,17 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name, } /* We reached the end without finding anything. */ - errormsg= "Could not find GTID state requested by slave in any binlog " + *errormsg= "Could not find GTID state requested by slave in any binlog " "files. Probably the slave state is too old and required binlog files " "have been purged."; + error= ER_MASTER_FATAL_ERROR_READING_BINLOG; end: if (glev) delete glev; free_root(&memroot, MYF(0)); - return errormsg; + return error; } @@ -1481,8 +1540,10 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags, slave_connection_state *until_gtid_state, enum_gtid_until_state *gtid_until_group, rpl_binlog_state *until_binlog_state, - bool slave_gtid_strict_mode, rpl_gtid *error_gtid, - bool *send_fake_gtid_list, HASH *fake_gtid_hash) + bool slave_gtid_strict_mode, + rpl_gtid *error_gtid, rpl_gtid *good_gtid, + bool *send_fake_gtid_list, HASH *fake_gtid_hash, + bool *first_event) { my_off_t pos; size_t len= packet->length(); @@ -1559,7 +1620,39 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags, event_gtid.seq_no <= gtid->seq_no) *gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ? GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION); - if (event_gtid.server_id == gtid->server_id && + if (slave_gtid_strict_mode) + { + if (event_gtid.seq_no == gtid->seq_no && + event_gtid.server_id != gtid->server_id) + { + my_errno= ER_SLAVE_IS_FROM_ALTERNATE_FUTURE; + *error_gtid= *gtid; + *good_gtid= event_gtid; + return "Requested slave GTID state have different server id " + "than the one in binlog"; + } + else if (*first_event && event_gtid.seq_no > gtid->seq_no) + { + /* + Earlier we decided that this binlog file has event that we need, + but the first event in the file has seq_no beyond what we need. + This can happen in two cases: + - this file is the first binlog we have and Gtid_list event + in it is empty; + - there is a gap between the last event mentioned in Gtid_list + and the first event in the binlog file. + As the first reason is the most probable one let's issue the + appropriate error message. + */ + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; + *error_gtid= *gtid; + return "Could not find GTID state requested by slave in any " + "binlog files. Probably the slave state is too old and " + "required binlog files have been purged"; + } + } + if ((event_gtid.server_id == gtid->server_id || + slave_gtid_strict_mode) && event_gtid.seq_no >= gtid->seq_no) { if (slave_gtid_strict_mode && event_gtid.seq_no > gtid->seq_no && @@ -1639,6 +1732,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags, } } } + *first_event = false; } /* @@ -1833,13 +1927,14 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, String slave_until_gtid_str(str_buf2, sizeof(str_buf2), system_charset_info); slave_connection_state gtid_state, until_gtid_state_obj; slave_connection_state *until_gtid_state= NULL; - rpl_gtid error_gtid; + rpl_gtid error_gtid, good_gtid; enum_gtid_skip_type gtid_skip_group= GTID_SKIP_NOT; enum_gtid_until_state gtid_until_group= GTID_UNTIL_NOT_DONE; rpl_binlog_state until_binlog_state; bool slave_gtid_strict_mode= false; bool send_fake_gtid_list= false; HASH fake_gtid_hash; + bool first_event = true; uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF; int old_max_allowed_packet= thd->variables.max_allowed_packet; @@ -1852,6 +1947,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, bzero((char*) &log,sizeof(log)); bzero(&error_gtid, sizeof(error_gtid)); + bzero(&good_gtid, sizeof(good_gtid)); my_hash_init(&fake_gtid_hash, &my_charset_bin, 32, offsetof(rpl_gtid, domain_id), sizeof(uint32), NULL, my_free, HASH_UNIQUE); @@ -1954,16 +2050,20 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, goto err; } if ((error= check_slave_start_position(thd, >id_state, &errmsg, - &error_gtid, until_gtid_state, - &fake_gtid_hash))) + &error_gtid, &good_gtid, + until_gtid_state, + &fake_gtid_hash, + slave_gtid_strict_mode))) { my_errno= error; goto err; } - if ((errmsg= gtid_find_binlog_file(>id_state, search_file_name, - until_gtid_state))) + if ((error= gtid_find_binlog_file(>id_state, &errmsg, &error_gtid, + &good_gtid, search_file_name, + until_gtid_state, + slave_gtid_strict_mode))) { - my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; + my_errno= error; goto err; } pos= 4; @@ -2254,8 +2354,10 @@ impossible position"; >id_state, >id_skip_group, until_gtid_state, >id_until_group, &until_binlog_state, - slave_gtid_strict_mode, &error_gtid, - &send_fake_gtid_list, &fake_gtid_hash))) + slave_gtid_strict_mode, + &error_gtid, &good_gtid, + &send_fake_gtid_list, &fake_gtid_hash, + &first_event))) { errmsg= tmp_msg; goto err; @@ -2449,9 +2551,10 @@ impossible position"; using_gtid_state, >id_state, >id_skip_group, until_gtid_state, >id_until_group, &until_binlog_state, - slave_gtid_strict_mode, &error_gtid, + slave_gtid_strict_mode, + &error_gtid, &good_gtid, &send_fake_gtid_list, - &fake_gtid_hash))) + &fake_gtid_hash, &first_event))) { errmsg= tmp_msg; goto err; @@ -2589,6 +2692,19 @@ err: /* Use this error code so slave will know not to try reconnect. */ my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG; } + else if (my_errno == ER_SLAVE_IS_FROM_ALTERNATE_FUTURE) + { + my_snprintf(error_text, sizeof(error_text), + "The slave provided a replication position with GTID " + "%u-%u-%llu. The server id (%u) in this GTID is different " + "from the server id (%d) in the GTID with the same domain id " + "and sequence number on the master. Slave appears to be from " + "an alternate future.", + error_gtid.domain_id, error_gtid.server_id, error_gtid.seq_no, + error_gtid.server_id, good_gtid.server_id); + /* Use this error code so slave will know not to try reconnect. */ + my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG; + } else if (my_errno == ER_CANNOT_LOAD_SLAVE_GTID_STATE) { my_snprintf(error_text, sizeof(error_text),