[Commits] 176f446338f: MDEV-21087: ER_SLAVE_INCIDENT arrives at slave without failure specifics
revision-id: 176f446338f5374233522b2e185e1d0acc422ffd (mariadb-10.2.30-21-g176f446338f) parent(s): 8129ff14407826d54745346c552fadf3d292a0d8 author: Sujatha committer: Sujatha timestamp: 2019-12-17 17:54:27 +0530 message: MDEV-21087: ER_SLAVE_INCIDENT arrives at slave without failure specifics Problem: ======= Issue 1: Incident event is reported for transactions which are rolled back successfully. Issue 2: When the mariadb slave (error) stops at receiving the incident event there's no description of what led to it. Neither in the event nor in the master's error log. Analysis: ========= Incident events are logged when a multi statement transaction specific changes cannot be safely rolled back. In reported scenario a big multi statement transaction is in progress and it makes use of innodb transactional engine. Row based replication is being used. At the time of writing the annotate event for the multi statement transaction the binary log cache size exceeds the configured max_binlog_cache_size. Hence write to binary log fails. The write error handling code generates an incident event and writes to binary log. This stops the slave. The error code is propagated to the callers and transaction is rolled back. Ideally binlog write error handling code should restrict incident event reporting only for cases where clean rollback is not possible. At the time of reporting incident event the actual error message which lead to incident should also be written to server error log for debugging purpose. Fix: === Report incident event in cases where transactions cannot be safely rolledback. Write the error message into the error log. @sql/handler.cc Added new variable "stmt_modified_non_trans_table" to identify modification to non transactional tables as "thd->transaction.stmt.modified_non_trans_table" and "thd->transaction.all.modified_non_trans_table" will be set at the later stage of execution. Annotate/Table_map events are written at the starting stage of transaction execution. i.e In a multi statement transaction when the first statement execution is complete its changes are written to binary log. At this stage the Annotate/Table_map events are written. To set "stmt_modified_non_trans_table" iterate through the list of tables modified by the transaction and see if they are transactional or not. If table is non transactional and it has write mode lock then set this variable to true. Upon event write error check for the above flag and report incident event. @sql/log.cc Report incident event if "stmt_modified_non_trans_table" variable is set. In "write_incident" code print thd->error_message() to server error log. --- .../extra/binlog_tests/binlog_write_error.inc | 2 + .../extra/rpl_tests/rpl_binlog_max_cache_size.test | 3 + mysql-test/suite/binlog/r/binlog_bug23533.result | 1 + .../suite/binlog/r/binlog_write_error.result | 1 + mysql-test/suite/binlog/t/binlog_bug23533.test | 2 +- .../binlog_encryption/binlog_write_error.result | 1 + .../rpl_mixed_binlog_max_cache_size.result | 2 + mysql-test/suite/rpl/r/rpl_mdev-11092.result | 70 +++++++++++- .../rpl/r/rpl_mixed_binlog_max_cache_size.result | 2 + .../rpl/r/rpl_stm_binlog_max_cache_size.result | 2 + mysql-test/suite/rpl/t/rpl_mdev-11092.test | 119 ++++++++++++++++++++- sql/handler.cc | 26 ++++- sql/log.cc | 54 +++++++--- sql/sql_class.h | 3 +- 14 files changed, 267 insertions(+), 21 deletions(-) diff --git a/mysql-test/extra/binlog_tests/binlog_write_error.inc b/mysql-test/extra/binlog_tests/binlog_write_error.inc index fa3ba087a7e..be5e9e0cfea 100644 --- a/mysql-test/extra/binlog_tests/binlog_write_error.inc +++ b/mysql-test/extra/binlog_tests/binlog_write_error.inc @@ -38,6 +38,8 @@ DROP TRIGGER IF EXISTS tr2; DROP VIEW IF EXISTS v1, v2; enable_warnings; +call mtr.add_suppression("Write to binary log failed: Error writing file*"); + --echo # --echo # Test injecting binlog write error when executing queries --echo # diff --git a/mysql-test/extra/rpl_tests/rpl_binlog_max_cache_size.test b/mysql-test/extra/rpl_tests/rpl_binlog_max_cache_size.test index 0f46b00f683..351b464de57 100644 --- a/mysql-test/extra/rpl_tests/rpl_binlog_max_cache_size.test +++ b/mysql-test/extra/rpl_tests/rpl_binlog_max_cache_size.test @@ -22,6 +22,9 @@ # ######################################################################################## call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); +call mtr.add_suppression("Write to binary log failed: Multi-row statements required more than .max_binlog_stmt_cache_size.* "); +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.* "); + let $old_max_binlog_cache_size= query_get_value(SHOW VARIABLES LIKE "max_binlog_cache_size", Value, 1); let $old_binlog_cache_size= query_get_value(SHOW VARIABLES LIKE "binlog_cache_size", Value, 1); diff --git a/mysql-test/suite/binlog/r/binlog_bug23533.result b/mysql-test/suite/binlog/r/binlog_bug23533.result index cc9799506c3..aa357b58f2c 100644 --- a/mysql-test/suite/binlog/r/binlog_bug23533.result +++ b/mysql-test/suite/binlog/r/binlog_bug23533.result @@ -1,3 +1,4 @@ +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.*"); SET AUTOCOMMIT=0; CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT, b TEXT, PRIMARY KEY(a)) ENGINE=InnoDB; SELECT COUNT(*) FROM t1; diff --git a/mysql-test/suite/binlog/r/binlog_write_error.result b/mysql-test/suite/binlog/r/binlog_write_error.result index 2606a9f40b3..ec1a701bd0e 100644 --- a/mysql-test/suite/binlog/r/binlog_write_error.result +++ b/mysql-test/suite/binlog/r/binlog_write_error.result @@ -9,6 +9,7 @@ DROP PROCEDURE IF EXISTS p2; DROP TRIGGER IF EXISTS tr1; DROP TRIGGER IF EXISTS tr2; DROP VIEW IF EXISTS v1, v2; +call mtr.add_suppression("Write to binary log failed: Error writing file*"); # # Test injecting binlog write error when executing queries # diff --git a/mysql-test/suite/binlog/t/binlog_bug23533.test b/mysql-test/suite/binlog/t/binlog_bug23533.test index ca610e399e4..a77497115e5 100644 --- a/mysql-test/suite/binlog/t/binlog_bug23533.test +++ b/mysql-test/suite/binlog/t/binlog_bug23533.test @@ -6,7 +6,7 @@ --source include/have_innodb.inc --source include/have_log_bin.inc --source include/have_binlog_format_row.inc - +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.*"); SET AUTOCOMMIT=0; # Create 1st table diff --git a/mysql-test/suite/binlog_encryption/binlog_write_error.result b/mysql-test/suite/binlog_encryption/binlog_write_error.result index 2606a9f40b3..ec1a701bd0e 100644 --- a/mysql-test/suite/binlog_encryption/binlog_write_error.result +++ b/mysql-test/suite/binlog_encryption/binlog_write_error.result @@ -9,6 +9,7 @@ DROP PROCEDURE IF EXISTS p2; DROP TRIGGER IF EXISTS tr1; DROP TRIGGER IF EXISTS tr2; DROP VIEW IF EXISTS v1, v2; +call mtr.add_suppression("Write to binary log failed: Error writing file*"); # # Test injecting binlog write error when executing queries # diff --git a/mysql-test/suite/binlog_encryption/rpl_mixed_binlog_max_cache_size.result b/mysql-test/suite/binlog_encryption/rpl_mixed_binlog_max_cache_size.result index 388c8e67b68..d302e73f15c 100644 --- a/mysql-test/suite/binlog_encryption/rpl_mixed_binlog_max_cache_size.result +++ b/mysql-test/suite/binlog_encryption/rpl_mixed_binlog_max_cache_size.result @@ -1,6 +1,8 @@ include/master-slave.inc [connection master] call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); +call mtr.add_suppression("Write to binary log failed: Multi-row statements required more than .max_binlog_stmt_cache_size.* "); +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.* "); SET GLOBAL max_binlog_cache_size = 4096; SET GLOBAL binlog_cache_size = 4096; SET GLOBAL max_binlog_stmt_cache_size = 4096; diff --git a/mysql-test/suite/rpl/r/rpl_mdev-11092.result b/mysql-test/suite/rpl/r/rpl_mdev-11092.result index 90b809477b2..ef392762eef 100644 --- a/mysql-test/suite/rpl/r/rpl_mdev-11092.result +++ b/mysql-test/suite/rpl/r/rpl_mdev-11092.result @@ -2,6 +2,9 @@ include/master-slave.inc [connection master] call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); call mtr.add_suppression("Slave SQL: The incident LOST_EVENTS occured on the master. .*"); +call mtr.add_suppression("Write to binary log failed: Multi-row statements required more than .max_binlog_stmt_cache_size.* "); +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.* "); +"*********** Annotate Event write failure **************" SET GLOBAL max_binlog_cache_size = 4096; SET GLOBAL binlog_cache_size = 4096; SET GLOBAL max_binlog_stmt_cache_size = 4096; @@ -10,12 +13,75 @@ disconnect master; connect master,127.0.0.1,root,,test,$MASTER_MYPORT,; CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=MYISAM; connection master; -ERROR HY000: Writing one row to the row-based binary log failed +"#######################################################################" +"# Test Case1: Annotate event write failure for MyISAM #" +"#######################################################################" +ERROR HY000: Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again include/wait_for_slave_sql_error_and_skip.inc [errno=1590] +"#######################################################################" +"# Test Case2: Annotate event write failure for INNODB #" +"#######################################################################" connection master; +CREATE TABLE t2(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=INNODB; +ERROR HY000: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again +"*** One row will be present as second row will be rolledback ***" +SELECT COUNT(*) FROM t2; +COUNT(*) +1 +connection slave; +SELECT COUNT(*) FROM t2; +COUNT(*) +1 +"#######################################################################" +"# Test Case3: Annotate event write failure for mixed engine UPDATE #" +"#######################################################################" +connection master; +ERROR HY000: Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again +include/wait_for_slave_sql_error_and_skip.inc [errno=1590] +connection master; +"****** Clean up *******" SET GLOBAL max_binlog_cache_size= ORIGINAL_VALUE; SET GLOBAL binlog_cache_size= ORIGINAL_VALUE; SET GLOBAL max_binlog_stmt_cache_size= ORIGINAL_VALUE; SET GLOBAL binlog_stmt_cache_size= ORIGINAL_VALUE; -DROP TABLE t1; +DROP TABLE t1,t2; +"*********** TABLE MAP Event write failure **************" +CREATE TABLE tm (f INT) ENGINE=MYISAM; +CREATE TABLE ti (f INT) ENGINE=INNODB; +INSERT INTO tm VALUES (10); +INSERT INTO ti VALUES (20); +connection slave; +"#######################################################################" +"# Test Case4: Table_map event write failure for mixed engine UPDATE #" +"#######################################################################" +connection master; +SET debug_dbug="+d,table_map_write_error"; +"In case of mixed engines if non trans table is updated write INCIDENT event" +UPDATE ti,tm SET tm.f=88, ti.f=120; +ERROR HY000: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again +include/wait_for_slave_sql_error_and_skip.inc [errno=1590] +"On Slave non trans table should be updated one row with f=88 should be found" +SELECT * FROM tm WHERE f=88; +f +88 +"On Innodb table 'ti' no update should be done no row exists with f=120" +SELECT COUNT(*) FROM ti WHERE f=120; +COUNT(*) +0 +"#######################################################################" +"# Test Case5: Table_map event write failure for trans engine UPDATE #" +"#######################################################################" +"Transaction will be rolled back. No incident event is written." +connection master; +UPDATE ti, tm set ti.f=30; +ERROR HY000: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again +"Verify on master that no rows exists with f=30" +SELECT COUNT(*) FROM ti WHERE f=30; +COUNT(*) +0 +connection slave; +connection master; +"******** Clean Up **********" +SET GLOBAL debug_dbug = ''; +DROP TABLE tm,ti; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_mixed_binlog_max_cache_size.result b/mysql-test/suite/rpl/r/rpl_mixed_binlog_max_cache_size.result index 388c8e67b68..d302e73f15c 100644 --- a/mysql-test/suite/rpl/r/rpl_mixed_binlog_max_cache_size.result +++ b/mysql-test/suite/rpl/r/rpl_mixed_binlog_max_cache_size.result @@ -1,6 +1,8 @@ include/master-slave.inc [connection master] call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); +call mtr.add_suppression("Write to binary log failed: Multi-row statements required more than .max_binlog_stmt_cache_size.* "); +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.* "); SET GLOBAL max_binlog_cache_size = 4096; SET GLOBAL binlog_cache_size = 4096; SET GLOBAL max_binlog_stmt_cache_size = 4096; diff --git a/mysql-test/suite/rpl/r/rpl_stm_binlog_max_cache_size.result b/mysql-test/suite/rpl/r/rpl_stm_binlog_max_cache_size.result index 388c8e67b68..d302e73f15c 100644 --- a/mysql-test/suite/rpl/r/rpl_stm_binlog_max_cache_size.result +++ b/mysql-test/suite/rpl/r/rpl_stm_binlog_max_cache_size.result @@ -1,6 +1,8 @@ include/master-slave.inc [connection master] call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); +call mtr.add_suppression("Write to binary log failed: Multi-row statements required more than .max_binlog_stmt_cache_size.* "); +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.* "); SET GLOBAL max_binlog_cache_size = 4096; SET GLOBAL binlog_cache_size = 4096; SET GLOBAL max_binlog_stmt_cache_size = 4096; diff --git a/mysql-test/suite/rpl/t/rpl_mdev-11092.test b/mysql-test/suite/rpl/t/rpl_mdev-11092.test index 31a385b40e6..6e130db37d8 100644 --- a/mysql-test/suite/rpl/t/rpl_mdev-11092.test +++ b/mysql-test/suite/rpl/t/rpl_mdev-11092.test @@ -1,3 +1,4 @@ +--source include/have_debug.inc --source include/have_innodb.inc --source include/not_embedded.inc --source include/not_windows.inc @@ -7,12 +8,16 @@ ######################################################################################## call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); call mtr.add_suppression("Slave SQL: The incident LOST_EVENTS occured on the master. .*"); +call mtr.add_suppression("Write to binary log failed: Multi-row statements required more than .max_binlog_stmt_cache_size.* "); +call mtr.add_suppression("Write to binary log failed: Multi-statement transaction required more than .max_binlog_cache_size.* "); let $old_max_binlog_cache_size= query_get_value(SHOW VARIABLES LIKE "max_binlog_cache_size", Value, 1); let $old_binlog_cache_size= query_get_value(SHOW VARIABLES LIKE "binlog_cache_size", Value, 1); let $old_max_binlog_stmt_cache_size= query_get_value(SHOW VARIABLES LIKE "max_binlog_stmt_cache_size", Value, 1); let $old_binlog_stmt_cache_size= query_get_value(SHOW VARIABLES LIKE "binlog_stmt_cache_size", Value, 1); +--echo "*********** Annotate Event write failure **************" + SET GLOBAL max_binlog_cache_size = 4096; SET GLOBAL binlog_cache_size = 4096; SET GLOBAL max_binlog_stmt_cache_size = 4096; @@ -26,8 +31,19 @@ let $data = `select concat('"', repeat('a',2000), '"')`; connection master; +# Insert a huge row into MyISAM table. The row will be inserted in engine and a +# request to write to binary log will be initiated. Since row annotations are +# enabled the size of the annotate event itself will exceed the +# "max_binlog_stmt_cache_size". This will result in ER_STMT_CACHE_FULL error +# and an incident event will be written to the binary log as row update in +# engine cannot be undone. + +--echo "#######################################################################" +--echo "# Test Case1: Annotate event write failure for MyISAM #" +--echo "#######################################################################" + --disable_query_log ---error ER_BINLOG_ROW_LOGGING_FAILED +--error ER_STMT_CACHE_FULL eval INSERT INTO t1 (a, data) VALUES (2, CONCAT($data, $data, $data, $data, $data, $data)); --enable_query_log @@ -37,8 +53,59 @@ eval INSERT INTO t1 (a, data) VALUES (2, --let $slave_sql_errno= 1590 --source include/wait_for_slave_sql_error_and_skip.inc -connection master; +# MDEV-21087 +# Insert two huge rows in to transaction cache. Have data such that first row +# fits inside the binary log cache. While writing the annotate event for the +# second row the binary log cache size will exceed "max_binlog_cache_size". +# Hence this statement cannot be written to binary log. As DMLs in Innodb can +# be safely rolled back only an error will be reported. Slave will continue to +# work. + +--echo "#######################################################################" +--echo "# Test Case2: Annotate event write failure for INNODB #" +--echo "#######################################################################" + +--connection master +CREATE TABLE t2(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=INNODB; +--disable_query_log +BEGIN; +eval INSERT INTO t2 (a, data) VALUES (1, CONCAT($data, $data)); +--error ER_TRANS_CACHE_FULL +eval INSERT INTO t2 (a, data) VALUES (2, CONCAT($data, $data)); +COMMIT; +--enable_query_log + +--echo "*** One row will be present as second row will be rolledback ***" +SELECT COUNT(*) FROM t2; +--sync_slave_with_master +SELECT COUNT(*) FROM t2; +# Testing mixed engine UPDATE statement scenario. In the following multi +# update query 'ha_update_row' will be invoked for t1 (myisam) table. This +# intern invokes binlog_write_table_map() function call. While writing a huge +# annotate event binary log cache size will exceed max_binlog_cache_size. +# Writing to binary log fails. Since non transactional changes cannot be +# rolled back incident event will be written to binary log. + +--echo "#######################################################################" +--echo "# Test Case3: Annotate event write failure for mixed engine UPDATE #" +--echo "#######################################################################" + +--connection master +let $new_data = `select concat('"', repeat('b',2000), '"')`; +--disable_query_log +--error ER_STMT_CACHE_FULL +eval UPDATE t1,t2 SET t1.data="Hello", t2.data=CONCAT($new_data,$new_data,$new_data,$new_data,$new_data); +--enable_query_log + +# Incident event +# 1590=ER_SLAVE_INCIDENT +--let $slave_sql_errno= 1590 +--source include/wait_for_slave_sql_error_and_skip.inc + +--connection master + +--echo "****** Clean up *******" --replace_result $old_max_binlog_cache_size ORIGINAL_VALUE --eval SET GLOBAL max_binlog_cache_size= $old_max_binlog_cache_size --replace_result $old_binlog_cache_size ORIGINAL_VALUE @@ -48,6 +115,52 @@ connection master; --replace_result $old_binlog_stmt_cache_size ORIGINAL_VALUE --eval SET GLOBAL binlog_stmt_cache_size= $old_binlog_stmt_cache_size -DROP TABLE t1; +DROP TABLE t1,t2; + +--echo "*********** TABLE MAP Event write failure **************" + +--let $debug_save= `SELECT @@GLOBAL.debug_dbug` +CREATE TABLE tm (f INT) ENGINE=MYISAM; +CREATE TABLE ti (f INT) ENGINE=INNODB; +INSERT INTO tm VALUES (10); +INSERT INTO ti VALUES (20); +--sync_slave_with_master + +--echo "#######################################################################" +--echo "# Test Case4: Table_map event write failure for mixed engine UPDATE #" +--echo "#######################################################################" +--connection master +SET debug_dbug="+d,table_map_write_error"; +--echo "In case of mixed engines if non trans table is updated write INCIDENT event" +--error ER_TRANS_CACHE_FULL +UPDATE ti,tm SET tm.f=88, ti.f=120; + +# Incident event +# 1590=ER_SLAVE_INCIDENT +--let $slave_sql_errno= 1590 +--source include/wait_for_slave_sql_error_and_skip.inc + +--echo "On Slave non trans table should be updated one row with f=88 should be found" +SELECT * FROM tm WHERE f=88; + +--echo "On Innodb table 'ti' no update should be done no row exists with f=120" +SELECT COUNT(*) FROM ti WHERE f=120; + +--echo "#######################################################################" +--echo "# Test Case5: Table_map event write failure for trans engine UPDATE #" +--echo "#######################################################################" +--echo "Transaction will be rolled back. No incident event is written." +--connection master +--error ER_TRANS_CACHE_FULL +UPDATE ti, tm set ti.f=30; +--echo "Verify on master that no rows exists with f=30" +SELECT COUNT(*) FROM ti WHERE f=30; + +--sync_slave_with_master + +--connection master +--echo "******** Clean Up **********" +--eval SET GLOBAL debug_dbug = '$debug_save' +DROP TABLE tm,ti; --source include/rpl_end.inc diff --git a/sql/handler.cc b/sql/handler.cc index 914a4dc07b1..6cd5ab15b36 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5863,7 +5863,27 @@ static int write_locked_table_maps(THD *thd) continue; TABLE **const end_ptr= lock->table + lock->table_count; - for (TABLE **table_ptr= lock->table ; + bool stmt_modified_non_trans_table= false; + /* + Iterate through list of tables and identify if multi statement + transaction modifies any non transactional table. Set the + 'stmt_modified_non_trans_table' flag to 'true'. In case if there a + failure at the time of writing annotate/table_map event into the binary + log this flag will enable server to write an incident event into the + binary log. This additional flag is required as + 'thd->transaction.stmt.modified_non_trans_table' will not available at + the time of writing table_map/annotate event. + */ + for (TABLE **table_ptr= lock->table; table_ptr != end_ptr ; ++table_ptr) + { + TABLE *const table= *table_ptr; + if (!table->file->has_transactions() && table->current_lock == F_WRLCK) + { + stmt_modified_non_trans_table=true; + break; + } + } + for (TABLE **table_ptr= lock->table ; table_ptr != end_ptr ; ++table_ptr) { @@ -5887,8 +5907,10 @@ static int write_locked_table_maps(THD *thd) */ bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE || table->file->has_transactions(); + int const error= thd->binlog_write_table_map(table, has_trans, - &with_annotate); + &with_annotate, + stmt_modified_non_trans_table); /* If an error occurs, it is the responsibility of the caller to roll back the transaction. diff --git a/sql/log.cc b/sql/log.cc index 5fd384d55a0..a82808bc7b7 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -5673,14 +5673,14 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd) } /** - This function writes a table map to the binary log. + This function writes a table map to the binary log. Note that in order to keep the signature uniform with related methods, we use a redundant parameter to indicate whether a transactional table was changed or not. If with_annotate != NULL and *with_annotate = TRUE write also Annotate_rows before the table map. - + @param table a pointer to the table. @param is_transactional @c true indicates a transactional table, otherwise @c false a non-transactional. @@ -5688,7 +5688,8 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd) nonzero if an error pops up when writing the table map event. */ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, - my_bool *with_annotate) + my_bool *with_annotate, + bool stmt_modified_non_trans_table) { int error; DBUG_ENTER("THD::binlog_write_table_map"); @@ -5699,7 +5700,7 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, /* Ensure that all events in a GTID group are in the same cache */ if (variables.option_bits & OPTION_GTID_BEGIN) is_transactional= 1; - + /* Pre-conditions */ DBUG_ASSERT(is_current_stmt_binlog_format_row()); DBUG_ASSERT(WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open()); @@ -5726,17 +5727,34 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, /* Annotate event should be written not more than once */ *with_annotate= 0; if ((error= writer.write(&anno))) - { - if (my_errno == EFBIG) - cache_data->set_incident(); - DBUG_RETURN(error); - } + goto write_err; } + DBUG_EXECUTE_IF("table_map_write_error", + { + if (is_transactional) + { + my_errno= EFBIG; + error= 1; + goto write_err; + } + }); if ((error= writer.write(&the_event))) - DBUG_RETURN(error); + goto write_err; - binlog_table_maps++; - DBUG_RETURN(0); + binlog_table_maps++; + DBUG_RETURN(0); + +write_err: + mysql_bin_log.set_write_error(this, is_transactional); + /* + For non-transactional engine or multi statement transaction with mixed + engines, data is written to table but writing to binary log failed. In + these scenarios rollback is not possible. Hence report an incident. + */ + if (mysql_bin_log.check_write_error(this) && cache_data && + stmt_modified_non_trans_table) + cache_data->set_incident(); + DBUG_RETURN(error); } /** @@ -7198,6 +7216,18 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd) mysql_mutex_unlock(&LOCK_log); } + /* + Upon writing incident event, check for thd->error() and print the + relevant error message in the error log. + */ + if (!error && thd->is_error()) + { + sql_print_error("Write to binary log failed: " + "%s. An incident event is written to binary log " + "and slave will be stopped.\n", + thd->get_stmt_da()->message()); + } + DBUG_RETURN(error); } diff --git a/sql/sql_class.h b/sql/sql_class.h index b35f9a93238..133bc9de607 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2423,7 +2423,8 @@ class THD :public Statement, void binlog_start_trans_and_stmt(); void binlog_set_stmt_begin(); int binlog_write_table_map(TABLE *table, bool is_transactional, - my_bool *with_annotate= 0); + my_bool *with_annotate= 0, + bool stmt_modified_non_trans_table= false); int binlog_write_row(TABLE* table, bool is_transactional, const uchar *buf); int binlog_delete_row(TABLE* table, bool is_transactional,
participants (1)
-
sujatha