[MDEV-9160] rbr, savepoints, trigger, MyISAM Created: 2015-11-20  Updated: 2022-01-18

Status: Open
Project: MariaDB Server
Component/s: Replication, Triggers
Affects Version/s: 10.1
Fix Version/s: 10.1

Type: Bug Priority: Major
Reporter: Sergei Golubchik Assignee: Vicențiu Ciorbaru
Resolution: Unresolved Votes: 0
Labels: None

Sprint: 10.1.13, 10.1.15, 10.2.2-1

 Description   

10.1.9 has merged a bug fix from the upstream:

69d4e72c

Bug#20901025: SLAVE ASSERTION IN UNPACK_ROW WITH ROLLBACK TO
SAVEPOINT IN ERROR HANDLER

This bug fix is incomplete. Adding INSERT INTO myisam_table to the "test case 6" makes the bug to reappear.



 Comments   
Comment by Vicențiu Ciorbaru [ 2016-06-26 ]

The failing test case is:

--source include/have_binlog_format_row.inc
--source include/have_innodb.inc
--source include/master-slave.inc
 
--echo # Test case 6:
--source include/rpl_connection_master.inc
CREATE TABLE t1 (f1 INTEGER ) ENGINE=INNODB;
CREATE TABLE t2 (f1 INTEGER ) ENGINE=INNODB;
CREATE TABLE t3 (f1 INTEGER ) ENGINE=MyISAM;
 
--delimiter |
 
CREATE FUNCTION f1() RETURNS INT
BEGIN
        SAVEPOINT event_logging_2;
        INSERT INTO t1 VALUES (1);
        INSERT INTO t3 VALUES (1);
        ROLLBACK TO event_logging_2;
        RETURN 0;
END|
 
--delimiter ;
 
BEGIN;
INSERT INTO t2 VALUES (1), (f1());
COMMIT;

Comment by Vicențiu Ciorbaru [ 2016-06-26 ]

This leads to an assertion failure in: sql/rpl_record.cc:233

DBUG_ASSERT(table_found);

However, there is a separate test case that leads to different binlog events that also causes a crash. The testcase for that involves only inserting (f1()) value into t2.

INSERT INTO t2 VALUES (f1());

This causes a crash in sql/log_event.cc:4243

DBUG_ASSERT(!current_stmt_is_commit || !rgi->tables_to_lock); 

The current_stmt_is_commit flag is set to true, while tables_to_lock is not NULL (it points to t3).

Comment by Vicențiu Ciorbaru [ 2016-06-28 ]

Found another test case that causes a crash. This time, we don't add the INSERT INTO t3 in the stored procedure, but just before it. The test case is:

--source include/have_binlog_format_row.inc
--source include/have_innodb.inc
--source include/master-slave.inc
 
--echo # Test case 6:
--source include/rpl_connection_master.inc
CREATE TABLE t1 (f1 INTEGER ) ENGINE=INNODB;
CREATE TABLE t2 (f1 INTEGER ) ENGINE=INNODB;
CREATE TABLE t3 (f1 INTEGER ) ENGINE=MyISAM;
 
--delimiter |
 
CREATE FUNCTION f1() RETURNS INT
BEGIN
        SAVEPOINT event_logging_2;
        INSERT INTO t1 VALUES (1);
        ROLLBACK TO event_logging_2;
        RETURN 0;
END|
 
--delimiter ;
 
BEGIN;
INSERT INTO t3 VALUES (1);
INSERT INTO t2 VALUES (1), (f1());
--source include/show_binlog_events.inc
COMMIT;
 
--source include/show_binlog_events.inc
--source include/sync_slave_sql_with_master.inc
--source include/rpl_connection_master.inc
 
#BEGIN;
#INSERT INTO t2 VALUES (1), (f1());
#COMMIT;
 
 
#--source include/rpl_connection_master.inc
#SELECT * FROM t2;
#SELECT * FROM t1;
#SELECT * FROM t2;
#SELECT * FROM t1;
 
--source include/rpl_connection_master.inc
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
DROP FUNCTION f1;

Comment by Vicențiu Ciorbaru [ 2016-06-28 ]

For our last test case, the generated binlog events are:

master-bin.000001	#	Gtid	#	#	BEGIN GTID #-#-#
master-bin.000001	#	Table_map	#	#	table_id: # (test.t3)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	COMMIT
master-bin.000001	#	Gtid	#	#	BEGIN GTID #-#-#
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	SAVEPOINT `event_logging_2`
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: #
master-bin.000001	#	Query	#	#	ROLLBACK TO `event_logging_2`
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Xid	#	#	COMMIT /* XID */

We get the DBUG_ASSERT(table_found) error during the final Write_rows_v1 event. The final write attempts to write to t2, the return value of the function.

The previous ROLLBACK event frees the rgi->tables_to_lock list, thus when searching that list for our table, we can not find it.

Comment by Vicențiu Ciorbaru [ 2016-06-28 ]

Now, an interesting difference is what happens when we have just:

BEGIN;
INSERT INTO t2 VALUES (1), (f1());
COMMIT;

In this case, the binlog looks different.

aster-bin.000001	#	Gtid	#	#	BEGIN GTID #-#-#
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	SAVEPOINT `event_logging_2`
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Xid	#	#	COMMIT /* XID */

I assume this is due to an optimization. Nonetheless, the binary log does not event attempt to write into t1 then rollback. It only writes to t2. Notice that there isn't even a ROLLBACK event present.

From a shallow understanding, it seems that we treat the case where we have non-transactional tables present in the transaction, different than the one where we only have transactional tables.

Here is the binary log for when instead of t3 we insert into t2 with 2 insert statements:

BEGIN;
INSERT INTO t2 VALUES (3);
INSERT INTO t2 VALUES (1), (f1());
COMMIT;

master-bin.000001	#	Gtid	#	#	BEGIN GTID #-#-#
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	SAVEPOINT `event_logging_2`
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Xid	#	#	COMMIT /* XID */

Again, no ROLLBACK event. One Write_rows_v1 event for each inserted value.

Comment by Vicențiu Ciorbaru [ 2016-06-28 ]

Potential fixes for when we do have a ROLLBACK event.

  • Create another Table_map event (or events), after the ROLLBACK statement.
  • Don't free the tables_to_lock when we're in a stored procedure.

The second option does not seem adequate because we might have other Query_log_events that when executing need to lock the tables that we're holding.

Comment by Vicențiu Ciorbaru [ 2016-06-28 ]

To test hypothesis of sending Table_map events after the ROLLBACK statement, it seems like we're already doing that. The following example shows it:

BEGIN;
INSERT INTO t3 VALUES (1), (2);
SAVEPOINT what_happens;
INSERT INTO t2 VALUES (3), (4), (5);
ROLLBACK TO what_happens;
INSERT INTO t2 VALUES (1), (f1());
COMMIT;

Gives us the binlog:

master-bin.000001	#	Gtid	#	#	BEGIN GTID #-#-#
master-bin.000001	#	Table_map	#	#	table_id: # (test.t3)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	COMMIT
master-bin.000001	#	Gtid	#	#	BEGIN GTID #-#-#
master-bin.000001	#	Query	#	#	SAVEPOINT `what_happens`
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	ROLLBACK TO `what_happens`
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	#	#	SAVEPOINT `event_logging_2`
master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
master-bin.000001	#	Table_map	#	#	table_id: # (test.t1)
master-bin.000001	#	Write_rows_v1	#	#	table_id: #
master-bin.000001	#	Query	#	#	ROLLBACK TO `event_logging_2`
master-bin.000001	#	Write_rows_v1	#	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Xid	#	#	COMMIT /* XID */

So, for regular ROLLBACKS within a transaction, we create the necessary Table map events.
For ROLLBACKS within triggers however, we don't.

Generated at Thu Feb 08 07:32:35 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.