[Maria-developers] 0664e1e766d: MDEV-22338: Assertion `!current_stmt_is_commit || !rgi->tables_to_lock' failed in Query_log_event::do_apply_event
revision-id: 0664e1e766d303187b381ace1c53547d1821e35c (mariadb-10.1.43-252-g0664e1e766d) parent(s): 85bd5314c56c150d756066806d4a6cb5b682383f author: Sujatha committer: Sujatha timestamp: 2020-08-10 12:29:48 +0530 message: MDEV-22338: Assertion `!current_stmt_is_commit || !rgi->tables_to_lock' failed in Query_log_event::do_apply_event Analysis: ========= With row based replication, master can generate event groups like shown below. BEGIN TABLE_MAP COMMIT i.e A binlog event group, without any rows event in it. Such groups are possible when a query operating on transactional table, doesn't modify any data in the table. For example a transaction is trying to modify both trans and non-trans tables and a trigger exists on non-transtable which modifies a trans table. CREATE TABLE t1 (id int) engine=innodb; CREATE TABLE t2 (id int) engine=myisam; CREATE TRIGGER t2 BEFORE INSERT ON t2 FOR EACH ROW DELETE FROM t1 LIMIT 1; INSERT INTO t2 VALUES (1); Above insert will write a record in 't2' table and its corresponding binlog entry is generated. First table map events are added to appropriate caches. t2's Table_Map is written into statement_cache. t1's Table_Map is written into transaction_cache. 'Write_rows' event is added to t2's statement_cache. Since there are no rows to remove from table 't1' there will not be any 'Delete_rows' event in the transaction cache. There won't be any 'Xid' event as well. During flush operation 'COMMIT' will be added to the transaction cache to complete the group. "t2's statement_cache" "t1's transaction_cache" BEGIN BEGIN TABLE_MAP TABLE_MAP WRITE_ROWS COMMIT COMMIT On the slave side it has strict rules, that a 'Table_Map' event should be followed by a rows event. This rule gets violated for 't1' transaction and results in an assert. Fix: ==== Replace the assert with if-else block. In a case where TABLE_MAP event is not followed by rows event invoke 'rows_event_stmt_cleanup' to initiate appropriate cleanup. The fix is based on upstream patch: commit da01f3846b62a2e41111256f9d6f0a2bb2de23c4 Author: Andrei Elkin <aelkin@mysql.com> --- .../suite/rpl/r/rpl_no_rows_event_assert.result | 26 ++++++++++++ .../suite/rpl/t/rpl_no_rows_event_assert.test | 46 ++++++++++++++++++++++ sql/log_event.cc | 22 ++++++++++- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_no_rows_event_assert.result b/mysql-test/suite/rpl/r/rpl_no_rows_event_assert.result new file mode 100644 index 00000000000..7661bb62305 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_no_rows_event_assert.result @@ -0,0 +1,26 @@ +include/master-slave.inc +[connection master] +######################################################################### +# Test case1: Transaction cache has 'Table_Map' event followed by # +# 'COMMIT'. # +######################################################################### +CREATE TABLE t1 (id int) ENGINE=INNODB; +CREATE TABLE t2 (id int) ENGINE=MYISAM; +CREATE TRIGGER t2 BEFORE INSERT ON t2 FOR EACH ROW DELETE FROM t1 LIMIT 1; +INSERT INTO t2 VALUES (1); +DROP TABLE t1,t2; +######################################################################### +# Test case2: Transaction cache has 'Query', 'Table_Map' followed by # +# 'COMMIT'. # +######################################################################### +CREATE TABLE t1(f INT) ENGINE=INNODB; +CREATE TABLE t2(f INT) ENGINE=MYISAM; +CREATE TABLE t3(id INT AUTO_INCREMENT, i INT, PRIMARY KEY (id)) ENGINE=INNODB; +CREATE TABLE t4(f INT) ENGINE=MYISAM; +CREATE TRIGGER trig1 BEFORE INSERT ON t2 FOR EACH ROW INSERT INTO t3(i) SELECT * FROM t4 LIMIT 0; +BEGIN; +DELETE FROM t1 WHERE f=0; +INSERT INTO t2 VALUES (2); +COMMIT; +DROP TABLE t1,t2,t3,t4; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_no_rows_event_assert.test b/mysql-test/suite/rpl/t/rpl_no_rows_event_assert.test new file mode 100644 index 00000000000..c5cc7634615 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_no_rows_event_assert.test @@ -0,0 +1,46 @@ +--source include/have_innodb.inc +--source include/have_binlog_format_mixed_or_row.inc +--source include/master-slave.inc +# ==== References ==== +# +# MDEV-22338: Assertion `!current_stmt_is_commit || !rgi->tables_to_lock' +# failed in Query_log_event::do_apply_event +# + +--echo ######################################################################### +--echo # Test case1: Transaction cache has 'Table_Map' event followed by # +--echo # 'COMMIT'. # +--echo ######################################################################### +--connection master +CREATE TABLE t1 (id int) ENGINE=INNODB; +CREATE TABLE t2 (id int) ENGINE=MYISAM; +CREATE TRIGGER t2 BEFORE INSERT ON t2 FOR EACH ROW DELETE FROM t1 LIMIT 1; +INSERT INTO t2 VALUES (1); +--sync_slave_with_master + +# Cleanup +--connection master +DROP TABLE t1,t2; + +--echo ######################################################################### +--echo # Test case2: Transaction cache has 'Query', 'Table_Map' followed by # +--echo # 'COMMIT'. # +--echo ######################################################################### + +CREATE TABLE t1(f INT) ENGINE=INNODB; +CREATE TABLE t2(f INT) ENGINE=MYISAM; +CREATE TABLE t3(id INT AUTO_INCREMENT, i INT, PRIMARY KEY (id)) ENGINE=INNODB; +CREATE TABLE t4(f INT) ENGINE=MYISAM; +CREATE TRIGGER trig1 BEFORE INSERT ON t2 FOR EACH ROW INSERT INTO t3(i) SELECT * FROM t4 LIMIT 0; + +BEGIN; +DELETE FROM t1 WHERE f=0; +INSERT INTO t2 VALUES (2); +COMMIT; +--sync_slave_with_master + +# Cleanup +--connection master +DROP TABLE t1,t2,t3,t4; + +--source include/rpl_end.inc diff --git a/sql/log_event.cc b/sql/log_event.cc index a1a442df43f..c37512aa6c1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4329,8 +4329,26 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, thd->clear_error(1); current_stmt_is_commit= is_commit(); - DBUG_ASSERT(!current_stmt_is_commit || !rgi->tables_to_lock); - rgi->slave_close_thread_tables(thd); + if (strcmp("COMMIT", query) == 0 && rgi->tables_to_lock != NULL) + { + /* + Cleaning-up the last statement context: + Found a row based event group which didn't modfiy any rows. i.e. + BEGIN + TABLE_MAP + COMMIT + */ + int error; + if ((error = rows_event_stmt_cleanup(rgi, thd))) + { + rli->report(ERROR_LEVEL, error, "Error in cleaning up after an event " + "preceding the commit; the group log file/position: %s %llu", + rli->group_master_log_name, + (ulong) rli->group_master_log_pos); + } + } + else + rgi->slave_close_thread_tables(thd); /* Note: We do not need to execute reset_one_shot_variables() if this
participants (1)
-
sujatha