commit 86720e7d5e792380533f8949cb6c63c4211cf56f Author: Vlad Lesin Date: Fri Jan 12 18:35:06 2018 +0300 Fix bug 88057 / PS-1119 (Intermediary slave does not log master changes with binlog_rows_query_log_events) Let's consider the following function: int ha_blackhole::update_row(const uchar *old_data, uchar *new_data) { DBUG_ENTER("ha_blackhole::update_row"); THD *thd= ha_thd(); if (is_slave_applier(thd) && thd->query().str == NULL) DBUG_RETURN(0); DBUG_RETURN(HA_ERR_WRONG_COMMAND); } In the case if this function is invoked from slave thread and it's query string is empty, then it's supposed the function finishes successfully, otherwise some error is returned. The same logic exists in some other blackhole engine functions. If "binlog_rows_query_log_events = 1" binary log contains events with queries. The queries are ignored, but during "Rows_query_log_event" event execution (see Rows_query_log_event::do_apply_event()) thread query string is filled with the query from the event. And when then Update_rows_log_event::do_exec_row() is executed, sql thread has query in thd->m_query_string, and this condition: is_slave_applier(thd) && thd->query().str == NULL becomes false, what leads to errors during execution of blackhole engine functions such as ha_blackhole::update_row(). Such errors are output to error log and sql thread stops, as a result is't binary log is not filling. The question is what does this "is_slave_applier(thd) && thd->query().str == NULL" condition mean? I suppose the condition means that the currently processed relay log event type is not QUERY_ITEM. That is why I would propose the following fix: replace "thd->query().str == NULL" with "thd->rli_slave->rows_query_ev || thd->query() == NULL". (cherry picked from commit 8de5f70935eee7dc014b797fadb6d6a70464893e) diff --git a/mysql-test/suite/rpl_gtid/r/bug88057.result b/mysql-test/suite/rpl_gtid/r/bug88057.result new file mode 100644 index 00000000000..c2782639d9a --- /dev/null +++ b/mysql-test/suite/rpl_gtid/r/bug88057.result @@ -0,0 +1,89 @@ +include/rpl_init.inc [topology=1->2->3] +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +### +# Create table on server 1 +##### +[connection server_1] +CREATE TABLE t1(a INT NOT NULL) ENGINE=innodb; +### +# Turn table into black hole on server 2 +##### +include/sync_slave_sql_with_master.inc +SET sql_log_bin = 0; +ALTER TABLE t1 ENGINE = blackhole; +SET sql_log_bin = 1; +### +# Generate insert, update, delete on server 1 +##### +[connection server_1] +INSERT INTO t1 (a) values (1), (2), (3); +ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a); +UPDATE t1 SET a = 22 WHERE a = 2; +DELETE FROM t1 WHERE a = 3; +SELECT * FROM t1; +a +1 +22 +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE t1(a INT NOT NULL) ENGINE=innodb +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Rows_query # # # INSERT INTO t1 (a) values (1), (2), (3) +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Query # # use `test`; ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a) +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Rows_query # # # UPDATE t1 SET a = 22 WHERE a = 2 +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Rows_query # # # DELETE FROM t1 WHERE a = 3 +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Delete_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Xid # # COMMIT /* XID */ +### +# Sync server 2 with server 1 +##### +include/sync_slave_sql_with_master.inc +SELECT * FROM t1; +a +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 # Query # # use `test`; CREATE TABLE t1(a INT NOT NULL) ENGINE=innodb +slave-bin.000001 # Query # # BEGIN +slave-bin.000001 # Rows_query # # # INSERT INTO t1 (a) values (1), (2), (3) +slave-bin.000001 # Table_map # # table_id: # (test.t1) +slave-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +slave-bin.000001 # Xid # # COMMIT /* XID */ +slave-bin.000001 # Query # # use `test`; ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a) +slave-bin.000001 # Query # # BEGIN +slave-bin.000001 # Rows_query # # # UPDATE t1 SET a = 22 WHERE a = 2 +slave-bin.000001 # Table_map # # table_id: # (test.t1) +slave-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F +slave-bin.000001 # Xid # # COMMIT /* XID */ +slave-bin.000001 # Query # # BEGIN +slave-bin.000001 # Rows_query # # # DELETE FROM t1 WHERE a = 3 +slave-bin.000001 # Table_map # # table_id: # (test.t1) +slave-bin.000001 # Delete_rows # # table_id: # flags: STMT_END_F +slave-bin.000001 # Xid # # COMMIT /* XID */ +### +# Sync server 3 with server 2 +##### +include/sync_slave_sql_with_master.inc +SELECT * FROM t1; +a +1 +22 +### +# Cleanup +##### +[connection server_1] +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl_gtid/t/bug88057.cnf b/mysql-test/suite/rpl_gtid/t/bug88057.cnf new file mode 100644 index 00000000000..1934f5ca0bd --- /dev/null +++ b/mysql-test/suite/rpl_gtid/t/bug88057.cnf @@ -0,0 +1,29 @@ +!include ../my.cnf + +[mysqld.1] +enforce-gtid-consistency=ON +gtid-mode=ON +master-info-repository=TABLE +relay-log-info-repository=TABLE +binlog-rows-query-log-events= ON +log-slave-updates + +[mysqld.2] +enforce-gtid-consistency=ON +gtid-mode=ON +master-info-repository=TABLE +relay-log-info-repository=TABLE +binlog-rows-query-log-events= ON +log-slave-updates + +[mysqld.3] +enforce-gtid-consistency=ON +gtid-mode=ON +master-info-repository=TABLE +relay-log-info-repository=TABLE +binlog-rows-query-log-events= ON +log-slave-updates + +[ENV] +SERVER_MYPORT_3= @mysqld.3.port +SERVER_MYSOCK_3= @mysqld.3.socket diff --git a/mysql-test/suite/rpl_gtid/t/bug88057.test b/mysql-test/suite/rpl_gtid/t/bug88057.test new file mode 100644 index 00000000000..251be31d067 --- /dev/null +++ b/mysql-test/suite/rpl_gtid/t/bug88057.test @@ -0,0 +1,65 @@ +# +# Bug 88057 test case. +# +# Test if some_engine->blackhole->some_engine topology works good if +# binlog-rows-query-log-events = ON +# +--source include/have_binlog_format_row.inc +--source include/not_group_replication_plugin.inc +--source include/have_blackhole.inc + +--let $rpl_topology = 1->2->3 +--let $use_gtids = 1 +--source include/rpl_init.inc + +--echo ### +--echo # Create table on server 1 +--echo ##### +--let $rpl_connection_name = server_1 +--source include/rpl_connection.inc +CREATE TABLE t1(a INT NOT NULL) ENGINE=innodb; + +--echo ### +--echo # Turn table into black hole on server 2 +--echo ##### +--let $sync_slave_connection = server_2 +--source include/sync_slave_sql_with_master.inc +SET sql_log_bin = 0; +ALTER TABLE t1 ENGINE = blackhole; +SET sql_log_bin = 1; + +--echo ### +--echo # Generate insert, update, delete on server 1 +--echo ##### +--let $rpl_connection_name = server_1 +--source include/rpl_connection.inc +INSERT INTO t1 (a) values (1), (2), (3); +ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a); +UPDATE t1 SET a = 22 WHERE a = 2; +DELETE FROM t1 WHERE a = 3; +SELECT * FROM t1; +--source include/show_binlog_events.inc + +--echo ### +--echo # Sync server 2 with server 1 +--echo ##### +--let $sync_slave_connection = server_2 +--source include/sync_slave_sql_with_master.inc +SELECT * FROM t1; +--source include/show_binlog_events.inc + +--echo ### +--echo # Sync server 3 with server 2 +--echo ##### +--let $sync_slave_connection = server_3 +--source include/sync_slave_sql_with_master.inc +SELECT * FROM t1; + +--echo ### +--echo # Cleanup +--echo ##### +--let $rpl_connection_name = server_1 +--source include/rpl_connection.inc +DROP TABLE t1; + +--source include/rpl_end.inc diff --git a/storage/blackhole/ha_blackhole.cc b/storage/blackhole/ha_blackhole.cc index 3fb6e8a225e..c1184260742 100644 --- a/storage/blackhole/ha_blackhole.cc +++ b/storage/blackhole/ha_blackhole.cc @@ -28,6 +28,7 @@ #include "my_psi_config.h" #include "mysql/plugin.h" #include "mysql/psi/mysql_memory.h" +#include "sql/rpl_rli.h" // THD::rli_slave::rows_query_ev #include "sql/sql_class.h" // THD, SYSTEM_THREAD_SLAVE_* #include "template_utils.h" @@ -36,9 +37,14 @@ using std::unique_ptr; static PSI_memory_key bh_key_memory_blackhole_share; -static bool is_slave_applier(THD *thd) { - return thd->system_thread == SYSTEM_THREAD_SLAVE_SQL || - thd->system_thread == SYSTEM_THREAD_SLAVE_WORKER; +static inline bool is_slave_applier(const THD &thd) { + return thd.system_thread == SYSTEM_THREAD_SLAVE_SQL || + thd.system_thread == SYSTEM_THREAD_SLAVE_WORKER; +} + +static inline bool pretend_for_slave(const THD &thd) { + return is_slave_applier(thd) && + (thd.rli_slave->rows_query_ev || thd.query().str == NULL); } /* Static declarations for handlerton */ @@ -102,14 +108,14 @@ int ha_blackhole::write_row(uchar *) { int ha_blackhole::update_row(const uchar *, uchar *) { DBUG_ENTER("ha_blackhole::update_row"); THD *thd = ha_thd(); - if (is_slave_applier(thd) && thd->query().str == NULL) DBUG_RETURN(0); + if (pretend_for_slave(*thd)) DBUG_RETURN(0); DBUG_RETURN(HA_ERR_WRONG_COMMAND); } int ha_blackhole::delete_row(const uchar *) { DBUG_ENTER("ha_blackhole::delete_row"); THD *thd = ha_thd(); - if (is_slave_applier(thd) && thd->query().str == NULL) DBUG_RETURN(0); + if (pretend_for_slave(*thd)) DBUG_RETURN(0); DBUG_RETURN(HA_ERR_WRONG_COMMAND); } @@ -122,7 +128,7 @@ int ha_blackhole::rnd_next(uchar *) { int rc; DBUG_ENTER("ha_blackhole::rnd_next"); THD *thd = ha_thd(); - if (is_slave_applier(thd) && thd->query().str == NULL) + if (pretend_for_slave(*thd)) rc = 0; else rc = HA_ERR_END_OF_FILE; @@ -191,7 +197,7 @@ int ha_blackhole::index_read_map(uchar *, const uchar *, key_part_map, int rc; DBUG_ENTER("ha_blackhole::index_read"); THD *thd = ha_thd(); - if (is_slave_applier(thd) && thd->query().str == NULL) + if (pretend_for_slave(*thd)) rc = 0; else rc = HA_ERR_END_OF_FILE; @@ -203,7 +209,7 @@ int ha_blackhole::index_read_idx_map(uchar *, uint, const uchar *, key_part_map, int rc; DBUG_ENTER("ha_blackhole::index_read_idx"); THD *thd = ha_thd(); - if (is_slave_applier(thd) && thd->query().str == NULL) + if (pretend_for_slave(*thd)) rc = 0; else rc = HA_ERR_END_OF_FILE; @@ -214,7 +220,7 @@ int ha_blackhole::index_read_last_map(uchar *, const uchar *, key_part_map) { int rc; DBUG_ENTER("ha_blackhole::index_read_last"); THD *thd = ha_thd(); - if (is_slave_applier(thd) && thd->query().str == NULL) + if (pretend_for_slave(*thd)) rc = 0; else rc = HA_ERR_END_OF_FILE;