mirror of
https://github.com/MariaDB/server.git
synced 2025-02-09 23:24:11 +01:00
0ad52e4d6a
If replicating an event in ROW format, and InnoDB detects a deadlock while searching for a row, the row event will error and rollback in InnoDB and indicate that the binlog cache also needs to be cleared, i.e. by marking thd->transaction_rollback_request. In the normal case, this will trigger an error in Rows_log_event::do_apply_event() and cause a rollback. During the Rows_log_event::do_apply_event() cleanup of a successful event application, there is a DBUG_ASSERT in log_event_server.cc::rows_event_stmt_cleanup(), which sets the expectation that thd->transaction_rollback_request cannot be set because the general rollback (i.e. not the InnoDB rollback) should have happened already. However, if the replica is configured to skip deadlock errors, the rows event logic will clear the error and continue on, as if no error happened. This results in thd->transaction_rollback_request being set while in rows_event_stmt_cleanup(), thereby triggering the assertion. This patch fixes this in the following ways: 1) The assertion is invalid, and thereby removed. 2) The rollback case is forced in rows_event_stmt_cleanup() if transaction_rollback_request is set. Note the differing behavior between transactions which are skipped due to deadlock errors and other errors. When a transaction is skipped due to an ignored deadlock error, the entire transaction is rolled back and skipped (though note MDEV-33930 which allows statements in the same transaction after the deadlock-inducing one to commit). When a transaction is skipped due to ignoring a different error, only the erroring statements are rolled-back and skipped - the rest of the transaction will execute as normal. The effect of this can be seen in the test results. The added test case to rpl_skip_error.test shows that only statements which are ignored due to non-deadlock errors are ignored in larger transactions. A diff between rpl_temporary_error2_skip_all.result and rpl_temporary_error2.result shows that all statements in the errored transaction are rolled back (diff pasted below): : diff rpl_temporary_error2.result rpl_temporary_error2_skip_all.result 49c49 < 2 1 --- > 2 NULL 51c51 < 4 1 --- > 4 NULL 53c53 < * There will be two rows in t2 due to the retry. --- > * There will be one row in t2 because the ignored deadlock does not retry. 57d56 < 1 59c58 < 1 --- > 0 Reviewed By: ============ Andrei Elkin <andrei.elkin@mariadb.com>
215 lines
6.2 KiB
Text
215 lines
6.2 KiB
Text
# ==== Purpose ====
|
|
#
|
|
# Verify that --slave-skip-errors works correctly. The error messages
|
|
# specified by --slave-skip-errors on slave should be ignored. If
|
|
# such errors occur, they should not be reported and not cause the
|
|
# slave to stop. If a skipped-due-to-error statement is a part of a
|
|
# larger transaction, and the error is not a deadlock error, the rest
|
|
# of the transaction should still commit, with just the errored statement
|
|
# ignored (note transactions which are skipped due to deadlocks are
|
|
# rolled back fully, see rpl_temporary_error2_skip_all.test).
|
|
#
|
|
# ==== Method ====
|
|
#
|
|
# We run the slave with --slave-skip-errors=1062 (the code for
|
|
# duplicate key). Then we have two set of tests. In the first
|
|
# set, we insert value 1 in a table on the slave, and then, on
|
|
# master, we insert value 1 in the table. In the second set, we
|
|
# insert several values on the master, disable the binlog and
|
|
# delete one of the values and re-enable the binlog. Right after,
|
|
# we perform an update on the set of values in order to generate
|
|
# a duplicate key on the slave. The errors should be ignored on
|
|
# the slave.
|
|
#
|
|
# ==== Related bugs ====
|
|
#
|
|
# BUG#28839: Errors in strict mode silently stop SQL thread if --slave-skip-errors exists
|
|
# bug in this test: BUG#30594: rpl.rpl_skip_error is nondeterministic:
|
|
# BUG#39393: slave-skip-errors does not work when using ROW based replication
|
|
|
|
source include/have_innodb.inc;
|
|
source include/master-slave.inc;
|
|
|
|
--connection slave
|
|
let $initial_skipped_error= query_get_value(show global status like "Slave_skipped_errors", Value, 1);
|
|
|
|
connection master;
|
|
--echo ==== Test Without sql_mode=strict_trans_tables ====
|
|
|
|
create table t1 (n int not null primary key);
|
|
|
|
sync_slave_with_master;
|
|
insert into t1 values (1);
|
|
|
|
connection master;
|
|
# Here we expect (ignored) error, since 1 is already in slave table
|
|
insert into t1 values (1);
|
|
# These should work fine
|
|
insert into t1 values (2),(3);
|
|
|
|
sync_slave_with_master;
|
|
select * from t1 order by n;
|
|
|
|
--echo ==== Test With sql_mode=strict_trans_tables ====
|
|
insert into t1 values (7),(8);
|
|
connection master;
|
|
set sql_mode=strict_trans_tables;
|
|
insert into t1 values (7), (8), (9);
|
|
sync_slave_with_master;
|
|
select * from t1 order by n;
|
|
source include/check_slave_is_running.inc;
|
|
|
|
--echo ==== Clean Up ====
|
|
connection master;
|
|
drop table t1;
|
|
sync_slave_with_master;
|
|
# End of 4.1 tests
|
|
|
|
#
|
|
# #28839 Errors in strict mode silently stop SQL thread if --slave-skip-errors exists
|
|
#
|
|
connection master;
|
|
create table t1(a int primary key);
|
|
insert into t1 values (1),(2);
|
|
SET SQL_LOG_BIN=0;
|
|
delete from t1;
|
|
SET SQL_LOG_BIN=1;
|
|
set sql_mode=strict_trans_tables;
|
|
insert into t1 values (1), (2), (3);
|
|
|
|
sync_slave_with_master;
|
|
select * from t1;
|
|
source include/check_slave_is_running.inc;
|
|
|
|
|
|
--echo ==== Clean Up ====
|
|
|
|
connection master;
|
|
drop table t1;
|
|
sync_slave_with_master;
|
|
# End of 5.0 tests
|
|
|
|
#
|
|
# BUG#39393: slave-skip-errors does not work when using ROW based replication
|
|
#
|
|
--echo ==== Using Innodb ====
|
|
|
|
connection master;
|
|
|
|
SET SQL_LOG_BIN=0;
|
|
CREATE TABLE t1(id INT NOT NULL PRIMARY KEY, data INT) Engine=InnoDB;
|
|
SHOW CREATE TABLE t1;
|
|
SET SQL_LOG_BIN=1;
|
|
|
|
connection slave;
|
|
|
|
call mtr.add_suppression("Slave SQL.*Could not execute .*te_rows event on table test.t.; Duplicate entry.* error.* 1062");
|
|
|
|
CREATE TABLE t1(id INT NOT NULL PRIMARY KEY, data INT) Engine=InnoDB;
|
|
SHOW CREATE TABLE t1;
|
|
|
|
connection master;
|
|
|
|
INSERT INTO t1 VALUES(1, 1);
|
|
INSERT INTO t1 VALUES(2, 1);
|
|
INSERT INTO t1 VALUES(3, 1);
|
|
INSERT INTO t1 VALUES(4, 1);
|
|
|
|
SET SQL_LOG_BIN=0;
|
|
DELETE FROM t1 WHERE id = 4;
|
|
SET SQL_LOG_BIN=1;
|
|
UPDATE t1 SET id= id + 3, data = 2;
|
|
|
|
sync_slave_with_master;
|
|
|
|
let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
|
|
echo $error;
|
|
|
|
--echo **** We cannot execute a select as there are differences in the
|
|
--echo **** behavior between STMT and RBR.
|
|
|
|
--echo ==== Using MyIsam ====
|
|
|
|
connection master;
|
|
|
|
SET SQL_LOG_BIN=0;
|
|
CREATE TABLE t2(id INT NOT NULL PRIMARY KEY, data INT) Engine=MyIsam;
|
|
SHOW CREATE TABLE t2;
|
|
SET SQL_LOG_BIN=1;
|
|
|
|
connection slave;
|
|
|
|
CREATE TABLE t2(id INT NOT NULL PRIMARY KEY, data INT) Engine=MyIsam;
|
|
SHOW CREATE TABLE t2;
|
|
let $current_skipped_error= query_get_value(show global status like "Slave_skipped_errors", Value, 1);
|
|
--let $delta_skipped_error= `select $current_skipped_error - $initial_skipped_error from dual`
|
|
--echo # Slave_skipped_errros = $delta_skipped_error
|
|
connection master;
|
|
|
|
INSERT INTO t2 VALUES(1, 1);
|
|
INSERT INTO t2 VALUES(2, 1);
|
|
INSERT INTO t2 VALUES(3, 1);
|
|
INSERT INTO t2 VALUES(5, 1);
|
|
|
|
SET SQL_LOG_BIN=0;
|
|
DELETE FROM t2 WHERE id = 5;
|
|
SET SQL_LOG_BIN=1;
|
|
UPDATE t2 SET id= id + 3, data = 2;
|
|
|
|
sync_slave_with_master;
|
|
|
|
let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
|
|
echo $error;
|
|
|
|
let $current_skipped_error= query_get_value(show global status like "Slave_skipped_errors", Value, 1);
|
|
--let $delta_skipped_error= `select $current_skipped_error - $initial_skipped_error from dual`
|
|
--echo # Slave_skipped_errros = $delta_skipped_error
|
|
|
|
--echo **** We cannot execute a select as there are differences in the
|
|
--echo **** behavior between STMT and RBR.
|
|
|
|
|
|
--echo ****
|
|
--echo **** Ensure transactions which are skipped due to encountering a
|
|
--echo **** non-deadlock error which is present in --slave-skip-errors result
|
|
--echo **** in partially committed transactions
|
|
# Slave will insert 3 first, and master will insert 3 within a larger trx
|
|
--let $value_preexisting_on_slave= 3
|
|
|
|
--connection master
|
|
CREATE TABLE t3 (a INT UNIQUE) ENGINE=InnoDB;
|
|
|
|
--sync_slave_with_master
|
|
--connection slave
|
|
--eval INSERT INTO t3 VALUES ($value_preexisting_on_slave)
|
|
|
|
--connection master
|
|
BEGIN;
|
|
INSERT INTO t3 VALUES (1);
|
|
INSERT INTO t3 VALUES (2);
|
|
--eval INSERT INTO t3 VALUES ($value_preexisting_on_slave)
|
|
INSERT INTO t3 VALUES (4);
|
|
COMMIT;
|
|
--sync_slave_with_master
|
|
|
|
--echo **** Master and slave tables should have the same data, due to the
|
|
--echo **** partially replicated transaction's data overlapping with the data
|
|
--echo **** that pre-existed on the slave. That is, despite the transaction
|
|
--echo **** consisting of 4 statements, the errored statement should be ignored
|
|
--echo **** and the other 3 should commit successfully.
|
|
let $diff_tables=master:t3,slave:t3;
|
|
source include/diff_tables.inc;
|
|
|
|
--connection master
|
|
DROP TABLE t3;
|
|
|
|
|
|
--echo ==== Clean Up ====
|
|
|
|
connection master;
|
|
|
|
DROP TABLE t1;
|
|
DROP TABLE t2;
|
|
|
|
sync_slave_with_master;
|
|
--source include/rpl_end.inc
|