mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
BUG#26395: if crash during autocommit update to transactional table on master, slave fails
Now, every transaction (including autocommit transactions) start with a BEGIN and end with a COMMIT/ROLLBACK in the binlog. Added a test case, and updated lots of test case result files. mysql-test/t/rpl_transaction-master.opt: BitKeeper file /home/sven/bk/b26395-autocommit-xa/5.0-rpl/mysql-test/t/rpl_transaction-master.opt mysql-test/t/rpl_transaction-slave.opt: BitKeeper file /home/sven/bk/b26395-autocommit-xa/5.0-rpl/mysql-test/t/rpl_transaction-slave.opt mysql-test/r/mix_innodb_myisam_binlog.result: Updated result file mysql-test/r/multi_update.result: Updated result file mysql-test/r/rpl_transaction.result: New result file for new test case. mysql-test/r/sp_trans_log.result: Updated result file mysql-test/r/variables-big.result: Updated result file mysql-test/t/rpl_transaction.test: New test case. sql/log.cc: - Always write BEGIN and COMMIT around statements, even in autocommit mode. - Added comments for binlog_commit and binlog_rollback. sql/log_event.cc: Added debug trigger to avoid writing xid events to the binlog.
This commit is contained in:
@ -100,9 +100,10 @@ insert into t1 values(9);
|
|||||||
insert into t2 select * from t1;
|
insert into t2 select * from t1;
|
||||||
show binlog events from 98;
|
show binlog events from 98;
|
||||||
Log_name Pos Event_type Server_id End_log_pos Info
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
master-bin.000001 98 Query 1 # use `test`; insert into t1 values(9)
|
master-bin.000001 98 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 185 Xid 1 # COMMIT /* XID */
|
master-bin.000001 166 Query 1 # use `test`; insert into t1 values(9)
|
||||||
master-bin.000001 212 Query 1 # use `test`; insert into t2 select * from t1
|
master-bin.000001 253 Xid 1 # COMMIT /* XID */
|
||||||
|
master-bin.000001 280 Query 1 # use `test`; insert into t2 select * from t1
|
||||||
delete from t1;
|
delete from t1;
|
||||||
delete from t2;
|
delete from t2;
|
||||||
reset master;
|
reset master;
|
||||||
@ -111,19 +112,21 @@ begin;
|
|||||||
insert into t2 select * from t1;
|
insert into t2 select * from t1;
|
||||||
show binlog events from 98;
|
show binlog events from 98;
|
||||||
Log_name Pos Event_type Server_id End_log_pos Info
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
master-bin.000001 98 Query 1 # use `test`; insert into t1 values(10)
|
master-bin.000001 98 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 186 Xid 1 # COMMIT /* XID */
|
master-bin.000001 166 Query 1 # use `test`; insert into t1 values(10)
|
||||||
master-bin.000001 213 Query 1 # use `test`; insert into t2 select * from t1
|
master-bin.000001 254 Xid 1 # COMMIT /* XID */
|
||||||
|
master-bin.000001 281 Query 1 # use `test`; insert into t2 select * from t1
|
||||||
insert into t1 values(11);
|
insert into t1 values(11);
|
||||||
commit;
|
commit;
|
||||||
show binlog events from 98;
|
show binlog events from 98;
|
||||||
Log_name Pos Event_type Server_id End_log_pos Info
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
master-bin.000001 98 Query 1 # use `test`; insert into t1 values(10)
|
master-bin.000001 98 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 186 Xid 1 # COMMIT /* XID */
|
master-bin.000001 166 Query 1 # use `test`; insert into t1 values(10)
|
||||||
master-bin.000001 213 Query 1 # use `test`; insert into t2 select * from t1
|
master-bin.000001 254 Xid 1 # COMMIT /* XID */
|
||||||
master-bin.000001 307 Query 1 # use `test`; BEGIN
|
master-bin.000001 281 Query 1 # use `test`; insert into t2 select * from t1
|
||||||
master-bin.000001 375 Query 1 # use `test`; insert into t1 values(11)
|
master-bin.000001 375 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 463 Xid 1 # COMMIT /* XID */
|
master-bin.000001 443 Query 1 # use `test`; insert into t1 values(11)
|
||||||
|
master-bin.000001 531 Xid 1 # COMMIT /* XID */
|
||||||
alter table t2 engine=INNODB;
|
alter table t2 engine=INNODB;
|
||||||
delete from t1;
|
delete from t1;
|
||||||
delete from t2;
|
delete from t2;
|
||||||
@ -235,25 +238,29 @@ master-bin.000001 98 Query 1 # use `test`; BEGIN
|
|||||||
master-bin.000001 166 Query 1 # use `test`; insert into t1 values(16)
|
master-bin.000001 166 Query 1 # use `test`; insert into t1 values(16)
|
||||||
master-bin.000001 254 Query 1 # use `test`; insert into t1 values(18)
|
master-bin.000001 254 Query 1 # use `test`; insert into t1 values(18)
|
||||||
master-bin.000001 342 Xid 1 # COMMIT /* XID */
|
master-bin.000001 342 Xid 1 # COMMIT /* XID */
|
||||||
master-bin.000001 369 Query 1 # use `test`; delete from t1
|
master-bin.000001 369 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 446 Xid 1 # COMMIT /* XID */
|
master-bin.000001 437 Query 1 # use `test`; delete from t1
|
||||||
master-bin.000001 473 Query 1 # use `test`; delete from t2
|
master-bin.000001 514 Xid 1 # COMMIT /* XID */
|
||||||
master-bin.000001 550 Xid 1 # COMMIT /* XID */
|
master-bin.000001 541 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 577 Query 1 # use `test`; alter table t2 type=MyISAM
|
master-bin.000001 609 Query 1 # use `test`; delete from t2
|
||||||
master-bin.000001 666 Query 1 # use `test`; insert into t1 values (1)
|
master-bin.000001 686 Xid 1 # COMMIT /* XID */
|
||||||
master-bin.000001 754 Xid 1 # COMMIT /* XID */
|
master-bin.000001 713 Query 1 # use `test`; alter table t2 type=MyISAM
|
||||||
master-bin.000001 781 Query 1 # use `test`; insert into t2 values (20)
|
master-bin.000001 802 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 870 Query 1 # use `test`; drop table t1,t2
|
master-bin.000001 870 Query 1 # use `test`; insert into t1 values (1)
|
||||||
master-bin.000001 949 Query 1 # use `test`; create temporary table ti (a int) engine=innodb
|
master-bin.000001 958 Xid 1 # COMMIT /* XID */
|
||||||
master-bin.000001 1059 Query 1 # use `test`; insert into ti values(1)
|
master-bin.000001 985 Query 1 # use `test`; insert into t2 values (20)
|
||||||
master-bin.000001 1146 Xid 1 # COMMIT /* XID */
|
master-bin.000001 1074 Query 1 # use `test`; drop table t1,t2
|
||||||
master-bin.000001 1173 Query 1 # use `test`; create temporary table t1 (a int) engine=myisam
|
master-bin.000001 1153 Query 1 # use `test`; create temporary table ti (a int) engine=innodb
|
||||||
master-bin.000001 1283 Query 1 # use `test`; insert t1 values (1)
|
master-bin.000001 1263 Query 1 # use `test`; BEGIN
|
||||||
master-bin.000001 1366 Query 1 # use `test`; create table t0 (n int)
|
master-bin.000001 1331 Query 1 # use `test`; insert into ti values(1)
|
||||||
master-bin.000001 1452 Query 1 # use `test`; insert t0 select * from t1
|
master-bin.000001 1418 Xid 1 # COMMIT /* XID */
|
||||||
master-bin.000001 1541 Query 1 # use `test`; insert into t0 select GET_LOCK("lock1",null)
|
master-bin.000001 1445 Query 1 # use `test`; create temporary table t1 (a int) engine=myisam
|
||||||
master-bin.000001 1648 Query 1 # use `test`; create table t2 (n int) engine=innodb
|
master-bin.000001 1555 Query 1 # use `test`; insert t1 values (1)
|
||||||
master-bin.000001 1748 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti`
|
master-bin.000001 1638 Query 1 # use `test`; create table t0 (n int)
|
||||||
|
master-bin.000001 1724 Query 1 # use `test`; insert t0 select * from t1
|
||||||
|
master-bin.000001 1813 Query 1 # use `test`; insert into t0 select GET_LOCK("lock1",null)
|
||||||
|
master-bin.000001 1920 Query 1 # use `test`; create table t2 (n int) engine=innodb
|
||||||
|
master-bin.000001 2020 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti`
|
||||||
do release_lock("lock1");
|
do release_lock("lock1");
|
||||||
drop table t0,t2;
|
drop table t0,t2;
|
||||||
reset master;
|
reset master;
|
||||||
@ -402,7 +409,7 @@ insert into t2 values (bug27417(1));
|
|||||||
ERROR 23000: Duplicate entry '1' for key 1
|
ERROR 23000: Duplicate entry '1' for key 1
|
||||||
show master status /* the offset must denote there is the query */;
|
show master status /* the offset must denote there is the query */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 267
|
master-bin.000001 335
|
||||||
select count(*) from t1 /* must be 1 */;
|
select count(*) from t1 /* must be 1 */;
|
||||||
count(*)
|
count(*)
|
||||||
1
|
1
|
||||||
@ -414,7 +421,7 @@ insert into t2 select bug27417(1) union select bug27417(2);
|
|||||||
ERROR 23000: Duplicate entry '2' for key 1
|
ERROR 23000: Duplicate entry '2' for key 1
|
||||||
show master status /* the offset must denote there is the query */;
|
show master status /* the offset must denote there is the query */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 290
|
master-bin.000001 358
|
||||||
select count(*) from t1 /* must be 2 */;
|
select count(*) from t1 /* must be 2 */;
|
||||||
count(*)
|
count(*)
|
||||||
2
|
2
|
||||||
@ -438,7 +445,7 @@ UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */;
|
|||||||
ERROR 23000: Duplicate entry '2' for key 1
|
ERROR 23000: Duplicate entry '2' for key 1
|
||||||
show master status /* the offset must denote there is the query */;
|
show master status /* the offset must denote there is the query */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 301
|
master-bin.000001 369
|
||||||
select count(*) from t1 /* must be 4 */;
|
select count(*) from t1 /* must be 4 */;
|
||||||
count(*)
|
count(*)
|
||||||
4
|
4
|
||||||
@ -466,7 +473,7 @@ delete from t2;
|
|||||||
ERROR 23000: Duplicate entry '1' for key 1
|
ERROR 23000: Duplicate entry '1' for key 1
|
||||||
show master status /* the offset must denote there is the query */;
|
show master status /* the offset must denote there is the query */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 246
|
master-bin.000001 314
|
||||||
select count(*) from t1 /* must be 1 */;
|
select count(*) from t1 /* must be 1 */;
|
||||||
count(*)
|
count(*)
|
||||||
1
|
1
|
||||||
@ -483,7 +490,7 @@ delete t2.* from t2,t5 where t2.a=t5.a + 1;
|
|||||||
ERROR 23000: Duplicate entry '1' for key 1
|
ERROR 23000: Duplicate entry '1' for key 1
|
||||||
show master status /* the offset must denote there is the query */;
|
show master status /* the offset must denote there is the query */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 274
|
master-bin.000001 342
|
||||||
select count(*) from t1 /* must be 1 */;
|
select count(*) from t1 /* must be 1 */;
|
||||||
count(*)
|
count(*)
|
||||||
1
|
1
|
||||||
@ -501,7 +508,7 @@ count(*)
|
|||||||
2
|
2
|
||||||
show master status /* the offset must denote there is the query */;
|
show master status /* the offset must denote there is the query */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 376
|
master-bin.000001 444
|
||||||
drop trigger trg_del_t2;
|
drop trigger trg_del_t2;
|
||||||
drop table t1,t2,t3,t4,t5;
|
drop table t1,t2,t3,t4,t5;
|
||||||
drop function bug27417;
|
drop function bug27417;
|
||||||
|
@ -545,7 +545,7 @@ a b
|
|||||||
4 4
|
4 4
|
||||||
show master status /* there must be the UPDATE query event */;
|
show master status /* there must be the UPDATE query event */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 260
|
master-bin.000001 328
|
||||||
delete from t1;
|
delete from t1;
|
||||||
delete from t2;
|
delete from t2;
|
||||||
insert into t1 values (1,2),(3,4),(4,4);
|
insert into t1 values (1,2),(3,4),(4,4);
|
||||||
@ -555,7 +555,7 @@ UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a;
|
|||||||
ERROR 23000: Duplicate entry '4' for key 1
|
ERROR 23000: Duplicate entry '4' for key 1
|
||||||
show master status /* there must be the UPDATE query event */;
|
show master status /* there must be the UPDATE query event */;
|
||||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||||
master-bin.000001 275
|
master-bin.000001 343
|
||||||
drop table t1, t2;
|
drop table t1, t2;
|
||||||
drop table if exists t1, t2, t3;
|
drop table if exists t1, t2, t3;
|
||||||
CREATE TABLE t1 (a int, PRIMARY KEY (a));
|
CREATE TABLE t1 (a int, PRIMARY KEY (a));
|
||||||
|
95
mysql-test/r/rpl_transaction.result
Normal file
95
mysql-test/r/rpl_transaction.result
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
stop slave;
|
||||||
|
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||||
|
reset master;
|
||||||
|
reset slave;
|
||||||
|
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||||
|
start slave;
|
||||||
|
CREATE TABLE tmyisam (a int) ENGINE = MYISAM;
|
||||||
|
CREATE TABLE tinnodb (a int) ENGINE = INNODB;
|
||||||
|
SHOW CREATE TABLE tmyisam;
|
||||||
|
Table Create Table
|
||||||
|
tmyisam CREATE TABLE `tmyisam` (
|
||||||
|
`a` int(11) default NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
SHOW CREATE TABLE tinnodb;
|
||||||
|
Table Create Table
|
||||||
|
tinnodb CREATE TABLE `tinnodb` (
|
||||||
|
`a` int(11) default NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1
|
||||||
|
==== Test 1: Non-XA Engines ====
|
||||||
|
--- on master ---
|
||||||
|
SET AUTOCOMMIT = 1;
|
||||||
|
INSERT INTO tmyisam VALUES (1);
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO tmyisam VALUES (2);
|
||||||
|
INSERT INTO tmyisam VALUES (3);
|
||||||
|
COMMIT;
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO tmyisam VALUES (5);
|
||||||
|
INSERT INTO tmyisam VALUES (6);
|
||||||
|
ROLLBACK;
|
||||||
|
Warnings:
|
||||||
|
Warning 1196 Some non-transactional changed tables couldn't be rolled back
|
||||||
|
SELECT * FROM tmyisam ORDER BY a;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
5
|
||||||
|
6
|
||||||
|
--- on slave ---
|
||||||
|
SELECT * FROM tmyisam ORDER BY a;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
5
|
||||||
|
6
|
||||||
|
==== Test 2: Master crash before writing XID event on XA engine ====
|
||||||
|
--- on master ---
|
||||||
|
INSERT INTO tinnodb VALUES (1);
|
||||||
|
SELECT * FROM tinnodb ORDER BY a;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
--- on slave ---
|
||||||
|
STOP SLAVE;
|
||||||
|
SHOW SLAVE STATUS;
|
||||||
|
Slave_IO_State
|
||||||
|
Master_Host 127.0.0.1
|
||||||
|
Master_User root
|
||||||
|
Master_Port #
|
||||||
|
Connect_Retry 1
|
||||||
|
Master_Log_File master-bin.000001
|
||||||
|
Read_Master_Log_Pos #
|
||||||
|
Relay_Log_File #
|
||||||
|
Relay_Log_Pos #
|
||||||
|
Relay_Master_Log_File master-bin.000001
|
||||||
|
Slave_IO_Running No
|
||||||
|
Slave_SQL_Running No
|
||||||
|
Replicate_Do_DB
|
||||||
|
Replicate_Ignore_DB
|
||||||
|
Replicate_Do_Table
|
||||||
|
Replicate_Ignore_Table #
|
||||||
|
Replicate_Wild_Do_Table
|
||||||
|
Replicate_Wild_Ignore_Table
|
||||||
|
Last_Errno 0
|
||||||
|
Last_Error
|
||||||
|
Skip_Counter 0
|
||||||
|
Exec_Master_Log_Pos #
|
||||||
|
Relay_Log_Space #
|
||||||
|
Until_Condition None
|
||||||
|
Until_Log_File
|
||||||
|
Until_Log_Pos 0
|
||||||
|
Master_SSL_Allowed No
|
||||||
|
Master_SSL_CA_File
|
||||||
|
Master_SSL_CA_Path
|
||||||
|
Master_SSL_Cert
|
||||||
|
Master_SSL_Cipher
|
||||||
|
Master_SSL_Key
|
||||||
|
Seconds_Behind_Master #
|
||||||
|
SELECT * FROM tinnodb ORDER BY a;
|
||||||
|
a
|
||||||
|
DROP TABLE tmyisam;
|
||||||
|
DROP TABLE tinnodb;
|
||||||
|
DROP TABLE tmyisam;
|
||||||
|
DROP TABLE tinnodb;
|
@ -16,6 +16,7 @@ show binlog events from 98 /* with fixes for #23333 will show there are 2 querie
|
|||||||
Log_name Pos Event_type Server_id End_log_pos Info
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
master-bin.000001 # Query 1 # #
|
master-bin.000001 # Query 1 # #
|
||||||
master-bin.000001 # Query 1 # #
|
master-bin.000001 # Query 1 # #
|
||||||
|
master-bin.000001 # Query 1 # #
|
||||||
select count(*),@a from t1 /* must be 1,1 */|
|
select count(*),@a from t1 /* must be 1,1 */|
|
||||||
count(*) @a
|
count(*) @a
|
||||||
1 1
|
1 1
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
set session transaction_prealloc_size=1024*1024*1024*1;
|
set session transaction_prealloc_size=1024*1024*1024*1;
|
||||||
show processlist;
|
show processlist;
|
||||||
Id User Host db Command Time State Info
|
Id User Host db Command Time State Info
|
||||||
1 root localhost test Query 0 NULL show processlist
|
6 root localhost test Query 0 NULL show processlist
|
||||||
set session transaction_prealloc_size=1024*1024*1024*2;
|
set session transaction_prealloc_size=1024*1024*1024*2;
|
||||||
show processlist;
|
show processlist;
|
||||||
Id User Host db Command Time State Info
|
Id User Host db Command Time State Info
|
||||||
1 root localhost test Query 2 NULL show processlist
|
6 root localhost test Query 1 NULL show processlist
|
||||||
set session transaction_prealloc_size=1024*1024*1024*3;
|
set session transaction_prealloc_size=1024*1024*1024*3;
|
||||||
show processlist;
|
show processlist;
|
||||||
Id User Host db Command Time State Info
|
Id User Host db Command Time State Info
|
||||||
1 root localhost test Query 0 NULL show processlist
|
6 root localhost test Query 0 NULL show processlist
|
||||||
set session transaction_prealloc_size=1024*1024*1024*4;
|
set session transaction_prealloc_size=1024*1024*1024*4;
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Truncated incorrect transaction_prealloc_size value: '4294967296'
|
||||||
show processlist;
|
show processlist;
|
||||||
Id User Host db Command Time State Info
|
Id User Host db Command Time State Info
|
||||||
1 root localhost test Query 0 NULL show processlist
|
6 root localhost test Query 0 NULL show processlist
|
||||||
set session transaction_prealloc_size=1024*1024*1024*5;
|
set session transaction_prealloc_size=1024*1024*1024*5;
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Truncated incorrect transaction_prealloc_size value: '5368709120'
|
||||||
show processlist;
|
show processlist;
|
||||||
Id User Host db Command Time State Info
|
Id User Host db Command Time State Info
|
||||||
1 root localhost test Query 0 NULL show processlist
|
6 root localhost test Query 0 NULL show processlist
|
||||||
|
1
mysql-test/t/rpl_transaction-master.opt
Normal file
1
mysql-test/t/rpl_transaction-master.opt
Normal file
@ -0,0 +1 @@
|
|||||||
|
--innodb --debug=d,do_not_write_xid
|
1
mysql-test/t/rpl_transaction-slave.opt
Normal file
1
mysql-test/t/rpl_transaction-slave.opt
Normal file
@ -0,0 +1 @@
|
|||||||
|
--innodb
|
106
mysql-test/t/rpl_transaction.test
Normal file
106
mysql-test/t/rpl_transaction.test
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Tests that transactions are replicated correctly, with various
|
||||||
|
# combinations of non-transactional and transactional non-XA tables.
|
||||||
|
# Also tests that an XA transaction where the master crashes just
|
||||||
|
# before writing the XID log event is executed correctly. See below
|
||||||
|
# for implementation details.
|
||||||
|
|
||||||
|
# Note: this test should not exist in 5.1 or higher. It has been
|
||||||
|
# replaced by rpl_ndb_transaction.test, which tests a superset of what
|
||||||
|
# this test tests.
|
||||||
|
|
||||||
|
source include/have_innodb.inc;
|
||||||
|
source include/master-slave.inc;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE tmyisam (a int) ENGINE = MYISAM;
|
||||||
|
CREATE TABLE tinnodb (a int) ENGINE = INNODB;
|
||||||
|
|
||||||
|
SHOW CREATE TABLE tmyisam;
|
||||||
|
SHOW CREATE TABLE tinnodb;
|
||||||
|
|
||||||
|
|
||||||
|
--echo ==== Test 1: Non-XA Engines ====
|
||||||
|
# Test that everything works fine with non-XA engines. We just try
|
||||||
|
# all ways to do transactions involving ndb and/or myisam, with
|
||||||
|
# rollback or commit.
|
||||||
|
|
||||||
|
--echo --- on master ---
|
||||||
|
|
||||||
|
SET AUTOCOMMIT = 1;
|
||||||
|
|
||||||
|
INSERT INTO tmyisam VALUES (1);
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO tmyisam VALUES (2);
|
||||||
|
INSERT INTO tmyisam VALUES (3);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO tmyisam VALUES (5);
|
||||||
|
INSERT INTO tmyisam VALUES (6);
|
||||||
|
--warning 1196
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
SELECT * FROM tmyisam ORDER BY a;
|
||||||
|
|
||||||
|
--echo --- on slave ---
|
||||||
|
--sync_slave_with_master
|
||||||
|
SELECT * FROM tmyisam ORDER BY a;
|
||||||
|
|
||||||
|
|
||||||
|
--echo ==== Test 2: Master crash before writing XID event on XA engine ====
|
||||||
|
# We now want to test the following scenario, to verify that BUG#26395
|
||||||
|
# has been fixed:
|
||||||
|
|
||||||
|
# "master and slave have a transactional table that uses XA. Master
|
||||||
|
# has AUTOCOMMIT on and executes a statement (in this case an
|
||||||
|
# INSERT). Master crashes just before writing the XID event."
|
||||||
|
|
||||||
|
# In this scenario, master will roll back, so slave should not execute
|
||||||
|
# the statement, and slave should roll back later when master is
|
||||||
|
# restarted.
|
||||||
|
|
||||||
|
# However, we the master to be alive so that we are sure it replicates
|
||||||
|
# the statement to the slave. So in the test case, we must therefore
|
||||||
|
# not crash the master. Instead, we fake the crash by just not writing
|
||||||
|
# the XID event to the binlog. This is done by the
|
||||||
|
# --debug=d,do_not_write_xid flag in the .opt file.
|
||||||
|
|
||||||
|
# So, unlike if the master had crashed, the master *will* execute the
|
||||||
|
# statement. But the slave should not execute it. Hence, after the
|
||||||
|
# first test is executed, the expected result on master is a table
|
||||||
|
# with one row, and on slave a table with no rows.
|
||||||
|
|
||||||
|
# To simulate the slave correctly, we wait until everything up to the
|
||||||
|
# XID is replicated. We cannot sync_slave_with_master, because that
|
||||||
|
# would wait for the transaction to end. Instead, we wait for
|
||||||
|
# "sufficiently long time". Then we stop the slave.
|
||||||
|
|
||||||
|
# Note: since this puts the master binlog in an inconsistent state,
|
||||||
|
# this should be the last test of the file.
|
||||||
|
|
||||||
|
--echo --- on master ---
|
||||||
|
--connection master
|
||||||
|
|
||||||
|
INSERT INTO tinnodb VALUES (1);
|
||||||
|
SELECT * FROM tinnodb ORDER BY a;
|
||||||
|
|
||||||
|
--echo --- on slave ---
|
||||||
|
--connection slave
|
||||||
|
--sleep 3
|
||||||
|
STOP SLAVE;
|
||||||
|
source include/wait_for_slave_to_stop.inc;
|
||||||
|
--replace_column 4 # 7 # 8 # 9 # 16 # 22 # 23 # 33 #
|
||||||
|
query_vertical SHOW SLAVE STATUS;
|
||||||
|
# the following statement should show that nothing has been replicated
|
||||||
|
SELECT * FROM tinnodb ORDER BY a;
|
||||||
|
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
connection master;
|
||||||
|
DROP TABLE tmyisam;
|
||||||
|
DROP TABLE tinnodb;
|
||||||
|
|
||||||
|
connection slave;
|
||||||
|
DROP TABLE tmyisam;
|
||||||
|
DROP TABLE tinnodb;
|
100
sql/log.cc
100
sql/log.cc
@ -122,6 +122,20 @@ static int binlog_prepare(THD *thd, bool all)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This function is called once after each statement.
|
||||||
|
|
||||||
|
It has the responsibility to flush the transaction cache to the
|
||||||
|
binlog file on commits.
|
||||||
|
|
||||||
|
@param thd The client thread that executes the transaction.
|
||||||
|
@param all true if this is the last statement before a COMMIT
|
||||||
|
statement; false if either this is a statement in a
|
||||||
|
transaction but not the last, or if this is a statement
|
||||||
|
not inside a BEGIN block and autocommit is on.
|
||||||
|
|
||||||
|
@see handlerton::commit
|
||||||
|
*/
|
||||||
static int binlog_commit(THD *thd, bool all)
|
static int binlog_commit(THD *thd, bool all)
|
||||||
{
|
{
|
||||||
IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
|
IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
|
||||||
@ -134,7 +148,15 @@ static int binlog_commit(THD *thd, bool all)
|
|||||||
// we're here because trans_log was flushed in MYSQL_LOG::log_xid()
|
// we're here because trans_log was flushed in MYSQL_LOG::log_xid()
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
if (all)
|
/*
|
||||||
|
Write commit event if at least one of the following holds:
|
||||||
|
- the user sends an explicit COMMIT; or
|
||||||
|
- the autocommit flag is on, and we are not inside a BEGIN.
|
||||||
|
However, if the user has not sent an explicit COMMIT, and we are
|
||||||
|
either inside a BEGIN or run with autocommit off, then this is not
|
||||||
|
the end of a transaction and we should not write a commit event.
|
||||||
|
*/
|
||||||
|
if (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
|
||||||
{
|
{
|
||||||
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
|
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
|
||||||
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
|
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
|
||||||
@ -144,6 +166,22 @@ static int binlog_commit(THD *thd, bool all)
|
|||||||
DBUG_RETURN(binlog_end_trans(thd, trans_log, &invisible_commit));
|
DBUG_RETURN(binlog_end_trans(thd, trans_log, &invisible_commit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This function is called when a transaction involving a transactional
|
||||||
|
table is rolled back.
|
||||||
|
|
||||||
|
It has the responsibility to flush the transaction cache to the
|
||||||
|
binlog file. However, if the transaction does not involve
|
||||||
|
non-transactional tables, nothing needs to be logged.
|
||||||
|
|
||||||
|
@param thd The client thread that executes the transaction.
|
||||||
|
@param all true if this is the last statement before a COMMIT
|
||||||
|
statement; false if either this is a statement in a
|
||||||
|
transaction but not the last, or if this is a statement
|
||||||
|
not inside a BEGIN block and autocommit is on.
|
||||||
|
|
||||||
|
@see handlerton::rollback
|
||||||
|
*/
|
||||||
static int binlog_rollback(THD *thd, bool all)
|
static int binlog_rollback(THD *thd, bool all)
|
||||||
{
|
{
|
||||||
int error=0;
|
int error=0;
|
||||||
@ -1817,9 +1855,11 @@ uint MYSQL_LOG::next_file_id()
|
|||||||
IMPLEMENTATION
|
IMPLEMENTATION
|
||||||
- To support transaction over replication, we wrap the transaction
|
- To support transaction over replication, we wrap the transaction
|
||||||
with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
|
with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
|
||||||
We want to write a BEGIN/ROLLBACK block when a non-transactional table
|
If a transaction that only involves transactional tables is
|
||||||
was updated in a transaction which was rolled back. This is to ensure
|
rolled back, we do not binlog it. However, we write a
|
||||||
that the same updates are run on the slave.
|
BEGIN/ROLLBACK block when a non-transactional table was updated
|
||||||
|
in a transaction which was rolled back. This is to ensure that
|
||||||
|
the same updates are run on the slave.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
|
bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
|
||||||
@ -1837,32 +1877,34 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
|
|||||||
byte header[LOG_EVENT_HEADER_LEN];
|
byte header[LOG_EVENT_HEADER_LEN];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Log "BEGIN" at the beginning of the transaction.
|
Log "BEGIN" at the beginning of every transaction. Here, a
|
||||||
which may contain more than 1 SQL statement.
|
transaction is either a BEGIN..COMMIT block or a single
|
||||||
|
statement in autocommit mode.
|
||||||
*/
|
*/
|
||||||
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
|
Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
|
||||||
{
|
/*
|
||||||
Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
|
Imagine this is rollback due to net timeout, after all
|
||||||
/*
|
statements of the transaction succeeded. Then we want a
|
||||||
Imagine this is rollback due to net timeout, after all statements of
|
zero-error code in BEGIN. In other words, if there was a
|
||||||
the transaction succeeded. Then we want a zero-error code in BEGIN.
|
really serious error code it's already in the statement's
|
||||||
In other words, if there was a really serious error code it's already
|
events, there is no need to put it also in this internally
|
||||||
in the statement's events, there is no need to put it also in this
|
generated event, and as this event is generated late it would
|
||||||
internally generated event, and as this event is generated late it
|
lead to false alarms.
|
||||||
would lead to false alarms.
|
|
||||||
This is safer than thd->clear_error() against kills at shutdown.
|
This is safer than thd->clear_error() against kills at shutdown.
|
||||||
*/
|
*/
|
||||||
qinfo.error_code= 0;
|
qinfo.error_code= 0;
|
||||||
/*
|
/*
|
||||||
Now this Query_log_event has artificial log_pos 0. It must be adjusted
|
Now this Query_log_event has artificial log_pos 0. It must be
|
||||||
to reflect the real position in the log. Not doing it would confuse the
|
adjusted to reflect the real position in the log. Not doing it
|
||||||
slave: it would prevent this one from knowing where he is in the
|
would confuse the slave: it would prevent this one from
|
||||||
master's binlog, which would result in wrong positions being shown to
|
knowing where he is in the master's binlog, which would result
|
||||||
the user, MASTER_POS_WAIT undue waiting etc.
|
in wrong positions being shown to the user, MASTER_POS_WAIT
|
||||||
*/
|
undue waiting etc.
|
||||||
if (qinfo.write(&log_file))
|
*/
|
||||||
goto err;
|
if (qinfo.write(&log_file))
|
||||||
}
|
goto err;
|
||||||
|
|
||||||
/* Read from the file used to cache the queries .*/
|
/* Read from the file used to cache the queries .*/
|
||||||
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
|
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -3793,6 +3793,7 @@ Xid_log_event(const char* buf,
|
|||||||
#ifndef MYSQL_CLIENT
|
#ifndef MYSQL_CLIENT
|
||||||
bool Xid_log_event::write(IO_CACHE* file)
|
bool Xid_log_event::write(IO_CACHE* file)
|
||||||
{
|
{
|
||||||
|
DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
|
||||||
return write_header(file, sizeof(xid)) ||
|
return write_header(file, sizeof(xid)) ||
|
||||||
my_b_safe_write(file, (byte*) &xid, sizeof(xid));
|
my_b_safe_write(file, (byte*) &xid, sizeof(xid));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user