mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 21:12:26 +01:00
d6bc2d60e5
Transaction on the slave sql thread got blocked against a slave's mysqld local ta's lock. Since the default, slave-transaction-retries=10, there was replaying of the replicated ta. That failed because of a new started from 5.0.13 policy not to rollback a timed-out transaction. Effectively the first round of a timed-out ta becomes committed by the replaying's first "BEGIN". It was decided to backport already existed method working in 5.1 implemented in bug #16228 for handling symmetrical deadlock problem. That patch introduced end_trans execution whenever a replicated ta deadlocks or timed-out. Note, that this solution can be practically suboptimal - in the light of the changed behavior due to timeout we still could replay only the last statement - only with a high rate of timeouting replicated transactions. mysql-test/r/rpl_deadlock.result: results changed mysql-test/t/rpl_deadlock.test: Refining the timeout part of the test to display that the timed-out transaction is rolled back prior its replaying by slave sql. Non-zero select's count would mean the first round work became persistent - wrong. sql/slave.cc: applying bug#16228 fix, approbated for deadlock use case in 5.1, almost verbatim. Another alternative to replay only the offending statement requires significant efforts, incl design work.
123 lines
3.3 KiB
Text
123 lines
3.3 KiB
Text
# See if slave restarts the transaction after failing on an InnoDB deadlock error.
|
|
|
|
# Note: testing what happens when too many retries is possible, but
|
|
# needs large waits when running with --debug, so we don't do it.
|
|
# The same way, this test may not test what is expected when run
|
|
# under Valgrind, timings are too short then (with --valgrind I
|
|
# (Guilhem) have seen the test manage to provoke lock wait timeout
|
|
# error but not deadlock error; that is ok as code deals with the two
|
|
# errors in exactly the same way.
|
|
# We don't 'show status like 'slave_retried_transactions'' because this
|
|
# is not repeatable (depends on sleeps).
|
|
|
|
source include/have_innodb.inc;
|
|
source include/master-slave.inc;
|
|
|
|
connection master;
|
|
create table t1 (a int not null, key(a)) engine=innodb;
|
|
create table t2 (a int not null, key(a)) engine=innodb;
|
|
# requiring 'unique' for the timeout part of the test
|
|
create table t3 (a int unique) engine=innodb;
|
|
create table t4 (a int) engine=innodb;
|
|
show variables like 'slave_transaction_retries';
|
|
sync_slave_with_master;
|
|
|
|
show create table t1;
|
|
show create table t2;
|
|
show variables like 'slave_transaction_retries';
|
|
stop slave;
|
|
|
|
# 1) Test deadlock
|
|
|
|
connection master;
|
|
begin;
|
|
# Let's keep BEGIN and the locked statement in two different relay logs.
|
|
let $1=200;disable_query_log;
|
|
while ($1)
|
|
{
|
|
eval insert into t3 values( $1 );
|
|
dec $1;
|
|
}
|
|
enable_query_log;
|
|
insert into t3 select * from t2 for update;
|
|
insert into t1 values(1);
|
|
commit;
|
|
save_master_pos;
|
|
|
|
connection slave;
|
|
begin;
|
|
# Let's make our transaction large so that it's slave who is chosen as
|
|
# victim
|
|
let $1=1000;
|
|
disable_query_log;
|
|
while ($1)
|
|
{
|
|
eval insert into t4 values( $1 );
|
|
dec $1;
|
|
}
|
|
enable_query_log;
|
|
select * from t1 for update;
|
|
start slave;
|
|
--real_sleep 3 # hope that slave is blocked now
|
|
insert into t2 values(201); # provoke deadlock, slave should be victim
|
|
commit;
|
|
sync_with_master;
|
|
select * from t1; # check that slave succeeded finally
|
|
select * from t2;
|
|
# check that no error is reported
|
|
--replace_column 1 # 8 # 9 # 23 # 33 #
|
|
--replace_result $MASTER_MYPORT MASTER_MYPORT
|
|
--vertical_results
|
|
show slave status;
|
|
--horizontal_results
|
|
|
|
# 2) Test lock wait timeout
|
|
|
|
stop slave;
|
|
delete from t3;
|
|
change master to master_log_pos=539; # the BEGIN log event
|
|
begin;
|
|
select * from t2 for update; # hold lock
|
|
start slave;
|
|
--real_sleep 10 # slave should have blocked, and be retrying
|
|
select count(*) from t3 /* must be zero */; # replaying begins after rollback
|
|
commit;
|
|
sync_with_master;
|
|
select * from t1; # check that slave succeeded finally
|
|
select * from t2;
|
|
# check that no error is reported
|
|
--replace_column 1 # 8 # 9 # 11 # 23 # 33 #
|
|
--replace_result $MASTER_MYPORT MASTER_MYPORT
|
|
--vertical_results
|
|
show slave status;
|
|
--horizontal_results
|
|
|
|
# Now we repeat 2), but with BEGIN in the same relay log as
|
|
# COMMIT (to see if seeking into hot log is ok).
|
|
|
|
set global max_relay_log_size=0;
|
|
|
|
# This is really copy-paste of 2) of above
|
|
stop slave;
|
|
delete from t3;
|
|
change master to master_log_pos=539;
|
|
begin;
|
|
select * from t2 for update;
|
|
start slave;
|
|
--real_sleep 10
|
|
select count(*) from t3 /* must be zero */; # replaying begins after rollback
|
|
commit;
|
|
sync_with_master;
|
|
select * from t1;
|
|
select * from t2;
|
|
--replace_column 1 # 8 # 9 # 11 # 23 # 33 #
|
|
--replace_result $MASTER_MYPORT MASTER_MYPORT
|
|
--vertical_results
|
|
show slave status;
|
|
--horizontal_results
|
|
|
|
connection master;
|
|
drop table t1,t2,t3,t4;
|
|
sync_slave_with_master;
|
|
|
|
# End of 4.1 tests
|