mirror of
https://github.com/MariaDB/server.git
synced 2025-08-05 13:16:09 +03:00
Merge MWL#116 into mariadb-5.2-rpl.
This commit is contained in:
63
mysql-test/r/group_commit.result
Normal file
63
mysql-test/r/group_commit.result
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
CREATE TABLE t1 (a VARCHAR(10) PRIMARY KEY) ENGINE=innodb;
|
||||||
|
SELECT variable_value INTO @commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_commits';
|
||||||
|
SELECT variable_value INTO @group_commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_group_commits';
|
||||||
|
SET DEBUG_SYNC= "commit_before_get_LOCK_commit_ordered SIGNAL group1_running WAIT_FOR group2_queued";
|
||||||
|
INSERT INTO t1 VALUES ("con1");
|
||||||
|
set DEBUG_SYNC= "now WAIT_FOR group1_running";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL group2_con2";
|
||||||
|
SET DEBUG_SYNC= "commit_after_release_LOCK_log WAIT_FOR group3_committed";
|
||||||
|
SET DEBUG_SYNC= "commit_after_group_run_commit_ordered SIGNAL group2_visible WAIT_FOR group2_checked";
|
||||||
|
INSERT INTO t1 VALUES ("con2");
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_con2";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL group2_con3";
|
||||||
|
INSERT INTO t1 VALUES ("con3");
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_con3";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL group2_con4";
|
||||||
|
INSERT INTO t1 VALUES ("con4");
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_con4";
|
||||||
|
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL group2_queued";
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
con1
|
||||||
|
SET DEBUG_SYNC= "commit_before_get_LOCK_commit_ordered SIGNAL group3_con5";
|
||||||
|
SET DEBUG_SYNC= "commit_after_get_LOCK_log SIGNAL con5_leader WAIT_FOR con6_queued";
|
||||||
|
INSERT INTO t1 VALUES ("con5");
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con5_leader";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL con6_queued";
|
||||||
|
INSERT INTO t1 VALUES ("con6");
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group3_con5";
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
con1
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL group3_committed";
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_visible";
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
con1
|
||||||
|
con2
|
||||||
|
con3
|
||||||
|
con4
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL group2_checked";
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
con1
|
||||||
|
con2
|
||||||
|
con3
|
||||||
|
con4
|
||||||
|
con5
|
||||||
|
con6
|
||||||
|
SELECT variable_value - @commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_commits';
|
||||||
|
variable_value - @commits
|
||||||
|
6
|
||||||
|
SELECT variable_value - @group_commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_group_commits';
|
||||||
|
variable_value - @group_commits
|
||||||
|
3
|
||||||
|
SET DEBUG_SYNC= 'RESET';
|
||||||
|
DROP TABLE t1;
|
35
mysql-test/r/group_commit_binlog_pos.result
Normal file
35
mysql-test/r/group_commit_binlog_pos.result
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
|
||||||
|
INSERT INTO t1 VALUES (0);
|
||||||
|
SET DEBUG_SYNC= "commit_after_get_LOCK_log SIGNAL con1_waiting WAIT_FOR con3_queued";
|
||||||
|
SET DEBUG_SYNC= "commit_loop_entry_commit_ordered SIGNAL con1_loop WAIT_FOR con1_loop_cont EXECUTE 3";
|
||||||
|
INSERT INTO t1 VALUES (1);
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_waiting";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL con2_queued";
|
||||||
|
INSERT INTO t1 VALUES (2);
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con2_queued";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL con3_queued";
|
||||||
|
INSERT INTO t1 VALUES (3);
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_loop";
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL con1_loop_cont";
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_loop";
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL con1_loop_cont";
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_loop";
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
SET SESSION debug="+d,crash_dispatch_command_before";
|
||||||
|
SELECT 1;
|
||||||
|
Got one of the listed errors
|
||||||
|
Got one of the listed errors
|
||||||
|
Got one of the listed errors
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
a
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
InnoDB: Last MySQL binlog file position 0 767, file name ./master-bin.000001
|
||||||
|
SET DEBUG_SYNC= 'RESET';
|
||||||
|
DROP TABLE t1;
|
120
mysql-test/r/group_commit_crash.result
Normal file
120
mysql-test/r/group_commit_crash.result
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
CREATE TABLE t1(a CHAR(255),
|
||||||
|
b CHAR(255),
|
||||||
|
c CHAR(255),
|
||||||
|
d CHAR(255),
|
||||||
|
id INT AUTO_INCREMENT,
|
||||||
|
PRIMARY KEY(id)) ENGINE=InnoDB;
|
||||||
|
create table t2 like t1;
|
||||||
|
create procedure setcrash(IN i INT)
|
||||||
|
begin
|
||||||
|
CASE i
|
||||||
|
WHEN 1 THEN SET SESSION debug="d,crash_commit_after_prepare";
|
||||||
|
WHEN 2 THEN SET SESSION debug="d,crash_commit_after_log";
|
||||||
|
WHEN 3 THEN SET SESSION debug="d,crash_commit_before_unlog";
|
||||||
|
WHEN 4 THEN SET SESSION debug="d,crash_commit_after";
|
||||||
|
WHEN 5 THEN SET SESSION debug="d,crash_commit_before";
|
||||||
|
ELSE BEGIN END;
|
||||||
|
END CASE;
|
||||||
|
end //
|
||||||
|
FLUSH TABLES;
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
RESET MASTER;
|
||||||
|
START TRANSACTION;
|
||||||
|
insert into t1 select * from t2;
|
||||||
|
call setcrash(5);
|
||||||
|
COMMIT;
|
||||||
|
Got one of the listed errors
|
||||||
|
SELECT * FROM t1 ORDER BY id;
|
||||||
|
a b c d id
|
||||||
|
SHOW BINLOG EVENTS LIMIT 2,1;
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
delete from t1;
|
||||||
|
RESET MASTER;
|
||||||
|
START TRANSACTION;
|
||||||
|
insert into t1 select * from t2;
|
||||||
|
call setcrash(4);
|
||||||
|
COMMIT;
|
||||||
|
Got one of the listed errors
|
||||||
|
SELECT * FROM t1 ORDER BY id;
|
||||||
|
a b c d id
|
||||||
|
a b c d 1
|
||||||
|
a b c d 2
|
||||||
|
a b c d 3
|
||||||
|
a b c d 4
|
||||||
|
a b c d 5
|
||||||
|
a b c d 6
|
||||||
|
a b c d 7
|
||||||
|
a b c d 8
|
||||||
|
a b c d 9
|
||||||
|
a b c d 10
|
||||||
|
SHOW BINLOG EVENTS LIMIT 2,1;
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
master-bin.000001 174 Query 1 268 use `test`; insert into t1 select * from t2
|
||||||
|
delete from t1;
|
||||||
|
RESET MASTER;
|
||||||
|
START TRANSACTION;
|
||||||
|
insert into t1 select * from t2;
|
||||||
|
call setcrash(3);
|
||||||
|
COMMIT;
|
||||||
|
Got one of the listed errors
|
||||||
|
SELECT * FROM t1 ORDER BY id;
|
||||||
|
a b c d id
|
||||||
|
a b c d 1
|
||||||
|
a b c d 2
|
||||||
|
a b c d 3
|
||||||
|
a b c d 4
|
||||||
|
a b c d 5
|
||||||
|
a b c d 6
|
||||||
|
a b c d 7
|
||||||
|
a b c d 8
|
||||||
|
a b c d 9
|
||||||
|
a b c d 10
|
||||||
|
SHOW BINLOG EVENTS LIMIT 2,1;
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
master-bin.000001 174 Query 1 268 use `test`; insert into t1 select * from t2
|
||||||
|
delete from t1;
|
||||||
|
RESET MASTER;
|
||||||
|
START TRANSACTION;
|
||||||
|
insert into t1 select * from t2;
|
||||||
|
call setcrash(2);
|
||||||
|
COMMIT;
|
||||||
|
Got one of the listed errors
|
||||||
|
SELECT * FROM t1 ORDER BY id;
|
||||||
|
a b c d id
|
||||||
|
a b c d 1
|
||||||
|
a b c d 2
|
||||||
|
a b c d 3
|
||||||
|
a b c d 4
|
||||||
|
a b c d 5
|
||||||
|
a b c d 6
|
||||||
|
a b c d 7
|
||||||
|
a b c d 8
|
||||||
|
a b c d 9
|
||||||
|
a b c d 10
|
||||||
|
SHOW BINLOG EVENTS LIMIT 2,1;
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
master-bin.000001 174 Query 1 268 use `test`; insert into t1 select * from t2
|
||||||
|
delete from t1;
|
||||||
|
RESET MASTER;
|
||||||
|
START TRANSACTION;
|
||||||
|
insert into t1 select * from t2;
|
||||||
|
call setcrash(1);
|
||||||
|
COMMIT;
|
||||||
|
Got one of the listed errors
|
||||||
|
SELECT * FROM t1 ORDER BY id;
|
||||||
|
a b c d id
|
||||||
|
SHOW BINLOG EVENTS LIMIT 2,1;
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
delete from t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP PROCEDURE setcrash;
|
28
mysql-test/suite/binlog/r/binlog_ioerr.result
Normal file
28
mysql-test/suite/binlog/r/binlog_ioerr.result
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
CALL mtr.add_suppression("Error writing file 'master-bin'");
|
||||||
|
RESET MASTER;
|
||||||
|
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
|
||||||
|
INSERT INTO t1 VALUES(0);
|
||||||
|
SET SESSION debug='+d,fail_binlog_write_1';
|
||||||
|
INSERT INTO t1 VALUES(1);
|
||||||
|
ERROR HY000: Error writing file 'master-bin' (errno: 28)
|
||||||
|
INSERT INTO t1 VALUES(2);
|
||||||
|
ERROR HY000: Error writing file 'master-bin' (errno: 28)
|
||||||
|
SET SESSION debug='';
|
||||||
|
INSERT INTO t1 VALUES(3);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
a
|
||||||
|
0
|
||||||
|
3
|
||||||
|
SHOW BINLOG EVENTS;
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
BINLOG POS Format_desc 1 ENDPOS Server ver: #, Binlog ver: #
|
||||||
|
BINLOG POS Query 1 ENDPOS use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb
|
||||||
|
BINLOG POS Query 1 ENDPOS BEGIN
|
||||||
|
BINLOG POS Query 1 ENDPOS use `test`; INSERT INTO t1 VALUES(0)
|
||||||
|
BINLOG POS Xid 1 ENDPOS COMMIT /* XID */
|
||||||
|
BINLOG POS Query 1 ENDPOS BEGIN
|
||||||
|
BINLOG POS Query 1 ENDPOS BEGIN
|
||||||
|
BINLOG POS Query 1 ENDPOS BEGIN
|
||||||
|
BINLOG POS Query 1 ENDPOS use `test`; INSERT INTO t1 VALUES(3)
|
||||||
|
BINLOG POS Xid 1 ENDPOS COMMIT /* XID */
|
||||||
|
DROP TABLE t1;
|
29
mysql-test/suite/binlog/t/binlog_ioerr.test
Normal file
29
mysql-test/suite/binlog/t/binlog_ioerr.test
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
source include/have_debug.inc;
|
||||||
|
source include/have_innodb.inc;
|
||||||
|
source include/have_log_bin.inc;
|
||||||
|
source include/have_binlog_format_mixed_or_statement.inc;
|
||||||
|
|
||||||
|
CALL mtr.add_suppression("Error writing file 'master-bin'");
|
||||||
|
|
||||||
|
RESET MASTER;
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
|
||||||
|
INSERT INTO t1 VALUES(0);
|
||||||
|
SET SESSION debug='+d,fail_binlog_write_1';
|
||||||
|
--error ER_ERROR_ON_WRITE
|
||||||
|
INSERT INTO t1 VALUES(1);
|
||||||
|
--error ER_ERROR_ON_WRITE
|
||||||
|
INSERT INTO t1 VALUES(2);
|
||||||
|
SET SESSION debug='';
|
||||||
|
INSERT INTO t1 VALUES(3);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
|
||||||
|
# Actually the output from this currently shows a bug.
|
||||||
|
# The injected IO error leaves partially written transactions in the binlog in
|
||||||
|
# the form of stray "BEGIN" events.
|
||||||
|
# These should disappear from the output if binlog error handling is improved.
|
||||||
|
--replace_regex /\/\* xid=.* \*\//\/* XID *\// /Server ver: .*, Binlog ver: .*/Server ver: #, Binlog ver: #/ /table_id: [0-9]+/table_id: #/
|
||||||
|
--replace_column 1 BINLOG 2 POS 5 ENDPOS
|
||||||
|
SHOW BINLOG EVENTS;
|
||||||
|
|
||||||
|
DROP TABLE t1;
|
31
mysql-test/suite/pbxt/r/pbxt_xa_binlog.result
Normal file
31
mysql-test/suite/pbxt/r/pbxt_xa_binlog.result
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
drop table if exists t1, t2;
|
||||||
|
SET binlog_format = 'mixed';
|
||||||
|
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
|
||||||
|
CREATE TABLE t2 (b INT PRIMARY KEY) ENGINE=pbxt;
|
||||||
|
BEGIN;
|
||||||
|
SELECT @@log_bin;
|
||||||
|
@@log_bin
|
||||||
|
1
|
||||||
|
INSERT INTO t1 VALUES (1);
|
||||||
|
INSERT INTO t2 VALUES (2);
|
||||||
|
COMMIT;
|
||||||
|
select * from t1;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
select * from t2;
|
||||||
|
b
|
||||||
|
2
|
||||||
|
SET sql_log_bin = 0;
|
||||||
|
INSERT INTO t1 VALUES (3);
|
||||||
|
INSERT INTO t2 VALUES (4);
|
||||||
|
COMMIT;
|
||||||
|
select * from t1 order by a;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
3
|
||||||
|
select * from t2 order by b;
|
||||||
|
b
|
||||||
|
2
|
||||||
|
4
|
||||||
|
drop table t1, t2;
|
||||||
|
drop database pbxt;
|
31
mysql-test/suite/pbxt/t/pbxt_xa_binlog.test
Normal file
31
mysql-test/suite/pbxt/t/pbxt_xa_binlog.test
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/have_log_bin.inc
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
drop table if exists t1, t2;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
SET binlog_format = 'mixed';
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
|
||||||
|
CREATE TABLE t2 (b INT PRIMARY KEY) ENGINE=pbxt;
|
||||||
|
BEGIN;
|
||||||
|
# verify that binlog is on
|
||||||
|
SELECT @@log_bin;
|
||||||
|
INSERT INTO t1 VALUES (1);
|
||||||
|
INSERT INTO t2 VALUES (2);
|
||||||
|
COMMIT;
|
||||||
|
select * from t1;
|
||||||
|
select * from t2;
|
||||||
|
|
||||||
|
# Test 2-phase commit when we disable binlogging.
|
||||||
|
SET sql_log_bin = 0;
|
||||||
|
INSERT INTO t1 VALUES (3);
|
||||||
|
INSERT INTO t2 VALUES (4);
|
||||||
|
COMMIT;
|
||||||
|
select * from t1 order by a;
|
||||||
|
select * from t2 order by b;
|
||||||
|
|
||||||
|
drop table t1, t2;
|
||||||
|
drop database pbxt;
|
||||||
|
|
115
mysql-test/t/group_commit.test
Normal file
115
mysql-test/t/group_commit.test
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
--source include/have_debug_sync.inc
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/have_log_bin.inc
|
||||||
|
|
||||||
|
# Test some group commit code paths by using debug_sync to do controlled
|
||||||
|
# commits of 6 transactions: first 1 alone, then 3 as a group, then 2 as a
|
||||||
|
# group.
|
||||||
|
#
|
||||||
|
# Group 3 is allowed to race as far as possible ahead before group 2 finishes
|
||||||
|
# to check some edge case for concurrency control.
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a VARCHAR(10) PRIMARY KEY) ENGINE=innodb;
|
||||||
|
|
||||||
|
SELECT variable_value INTO @commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_commits';
|
||||||
|
SELECT variable_value INTO @group_commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_group_commits';
|
||||||
|
|
||||||
|
connect(con1,localhost,root,,);
|
||||||
|
connect(con2,localhost,root,,);
|
||||||
|
connect(con3,localhost,root,,);
|
||||||
|
connect(con4,localhost,root,,);
|
||||||
|
connect(con5,localhost,root,,);
|
||||||
|
connect(con6,localhost,root,,);
|
||||||
|
|
||||||
|
# Start group1 (with one thread) doing commit, waiting for
|
||||||
|
# group2 to queue up before finishing.
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
SET DEBUG_SYNC= "commit_before_get_LOCK_commit_ordered SIGNAL group1_running WAIT_FOR group2_queued";
|
||||||
|
send INSERT INTO t1 VALUES ("con1");
|
||||||
|
|
||||||
|
# Make group2 (with three threads) queue up.
|
||||||
|
# Make sure con2 is the group commit leader for group2.
|
||||||
|
# Make group2 wait with running commit_ordered() until group3 has committed.
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
set DEBUG_SYNC= "now WAIT_FOR group1_running";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL group2_con2";
|
||||||
|
SET DEBUG_SYNC= "commit_after_release_LOCK_log WAIT_FOR group3_committed";
|
||||||
|
SET DEBUG_SYNC= "commit_after_group_run_commit_ordered SIGNAL group2_visible WAIT_FOR group2_checked";
|
||||||
|
send INSERT INTO t1 VALUES ("con2");
|
||||||
|
connection con3;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_con2";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL group2_con3";
|
||||||
|
send INSERT INTO t1 VALUES ("con3");
|
||||||
|
connection con4;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_con3";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL group2_con4";
|
||||||
|
send INSERT INTO t1 VALUES ("con4");
|
||||||
|
|
||||||
|
# When group2 is queued, let group1 continue and queue group3.
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_con4";
|
||||||
|
|
||||||
|
# At this point, trasaction 1 is still not visible as commit_ordered() has not
|
||||||
|
# been called yet.
|
||||||
|
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL group2_queued";
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
# Now transaction 1 is visible.
|
||||||
|
connection default;
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
|
||||||
|
connection con5;
|
||||||
|
SET DEBUG_SYNC= "commit_before_get_LOCK_commit_ordered SIGNAL group3_con5";
|
||||||
|
SET DEBUG_SYNC= "commit_after_get_LOCK_log SIGNAL con5_leader WAIT_FOR con6_queued";
|
||||||
|
send INSERT INTO t1 VALUES ("con5");
|
||||||
|
|
||||||
|
connection con6;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con5_leader";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL con6_queued";
|
||||||
|
send INSERT INTO t1 VALUES ("con6");
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group3_con5";
|
||||||
|
# Still only transaction 1 visible, as group2 have not yet run commit_ordered().
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL group3_committed";
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR group2_visible";
|
||||||
|
# Now transactions 1-4 visible.
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL group2_checked";
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
connection con3;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
connection con4;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
connection con5;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
connection con6;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
# Check all transactions finally visible.
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
|
||||||
|
SELECT variable_value - @commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_commits';
|
||||||
|
SELECT variable_value - @group_commits FROM information_schema.global_status
|
||||||
|
WHERE variable_name = 'binlog_group_commits';
|
||||||
|
|
||||||
|
SET DEBUG_SYNC= 'RESET';
|
||||||
|
DROP TABLE t1;
|
1
mysql-test/t/group_commit_binlog_pos-master.opt
Normal file
1
mysql-test/t/group_commit_binlog_pos-master.opt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
--skip-stack-trace --skip-core-file
|
85
mysql-test/t/group_commit_binlog_pos.test
Normal file
85
mysql-test/t/group_commit_binlog_pos.test
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
--source include/have_debug_sync.inc
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/have_log_bin.inc
|
||||||
|
--source include/have_binlog_format_mixed_or_statement.inc
|
||||||
|
|
||||||
|
# Need DBUG to crash the server intentionally
|
||||||
|
--source include/have_debug.inc
|
||||||
|
# Don't test this under valgrind, memory leaks will occur as we crash
|
||||||
|
--source include/not_valgrind.inc
|
||||||
|
|
||||||
|
# XtraDB stores the binlog position corresponding to the last commit, and
|
||||||
|
# prints it during crash recovery.
|
||||||
|
# Test that we get the correct position when we group commit several
|
||||||
|
# transactions together.
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
|
||||||
|
INSERT INTO t1 VALUES (0);
|
||||||
|
|
||||||
|
connect(con1,localhost,root,,);
|
||||||
|
connect(con2,localhost,root,,);
|
||||||
|
connect(con3,localhost,root,,);
|
||||||
|
|
||||||
|
# Queue up three commits for group commit.
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
SET DEBUG_SYNC= "commit_after_get_LOCK_log SIGNAL con1_waiting WAIT_FOR con3_queued";
|
||||||
|
SET DEBUG_SYNC= "commit_loop_entry_commit_ordered SIGNAL con1_loop WAIT_FOR con1_loop_cont EXECUTE 3";
|
||||||
|
send INSERT INTO t1 VALUES (1);
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_waiting";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL con2_queued";
|
||||||
|
send INSERT INTO t1 VALUES (2);
|
||||||
|
|
||||||
|
connection con3;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con2_queued";
|
||||||
|
SET DEBUG_SYNC= "commit_after_prepare_ordered SIGNAL con3_queued";
|
||||||
|
send INSERT INTO t1 VALUES (3);
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_loop";
|
||||||
|
# At this point, no transactions are committed.
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL con1_loop_cont";
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_loop";
|
||||||
|
# At this point, 1 transaction is committed.
|
||||||
|
SET DEBUG_SYNC= "now SIGNAL con1_loop_cont";
|
||||||
|
SET DEBUG_SYNC= "now WAIT_FOR con1_loop";
|
||||||
|
|
||||||
|
# At this point, 2 transactions are committed.
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
# Now crash the server with 1+2 in-memory committed, 3 only prepared.
|
||||||
|
connection default;
|
||||||
|
system echo wait-group_commit_binlog_pos.test >> $MYSQLTEST_VARDIR/tmp/mysqld.1.expect;
|
||||||
|
SET SESSION debug="+d,crash_dispatch_command_before";
|
||||||
|
--error 2006,2013
|
||||||
|
SELECT 1;
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
--error 2006,2013
|
||||||
|
reap;
|
||||||
|
connection con3;
|
||||||
|
--error 2006,2013
|
||||||
|
reap;
|
||||||
|
|
||||||
|
system echo restart-group_commit_binlog_pos.test >> $MYSQLTEST_VARDIR/tmp/mysqld.1.expect;
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
--enable_reconnect
|
||||||
|
--source include/wait_until_connected_again.inc
|
||||||
|
|
||||||
|
# Crash recovery should recover all three transactions.
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
|
||||||
|
# Check that the binlog position reported by InnoDB is the correct one
|
||||||
|
# for the end of the second transaction (as can be checked with
|
||||||
|
# mysqlbinlog).
|
||||||
|
let $MYSQLD_DATADIR= `SELECT @@datadir`;
|
||||||
|
--exec grep 'InnoDB: Last MySQL binlog file position' $MYSQLD_DATADIR/../../log/mysqld.1.err | tail -1
|
||||||
|
|
||||||
|
SET DEBUG_SYNC= 'RESET';
|
||||||
|
DROP TABLE t1;
|
1
mysql-test/t/group_commit_crash-master.opt
Normal file
1
mysql-test/t/group_commit_crash-master.opt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
--skip-stack-trace --skip-core-file
|
80
mysql-test/t/group_commit_crash.test
Normal file
80
mysql-test/t/group_commit_crash.test
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Testing group commit by crashing a few times.
|
||||||
|
# Test adapted from the Facebook patch: lp:mysqlatfacebook
|
||||||
|
--source include/not_embedded.inc
|
||||||
|
# Don't test this under valgrind, memory leaks will occur
|
||||||
|
--source include/not_valgrind.inc
|
||||||
|
|
||||||
|
# Binary must be compiled with debug for crash to occur
|
||||||
|
--source include/have_debug.inc
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/have_log_bin.inc
|
||||||
|
|
||||||
|
let $file_format_check=`SELECT @@innodb_file_format_check`;
|
||||||
|
CREATE TABLE t1(a CHAR(255),
|
||||||
|
b CHAR(255),
|
||||||
|
c CHAR(255),
|
||||||
|
d CHAR(255),
|
||||||
|
id INT AUTO_INCREMENT,
|
||||||
|
PRIMARY KEY(id)) ENGINE=InnoDB;
|
||||||
|
create table t2 like t1;
|
||||||
|
delimiter //;
|
||||||
|
create procedure setcrash(IN i INT)
|
||||||
|
begin
|
||||||
|
CASE i
|
||||||
|
WHEN 1 THEN SET SESSION debug="d,crash_commit_after_prepare";
|
||||||
|
WHEN 2 THEN SET SESSION debug="d,crash_commit_after_log";
|
||||||
|
WHEN 3 THEN SET SESSION debug="d,crash_commit_before_unlog";
|
||||||
|
WHEN 4 THEN SET SESSION debug="d,crash_commit_after";
|
||||||
|
WHEN 5 THEN SET SESSION debug="d,crash_commit_before";
|
||||||
|
ELSE BEGIN END;
|
||||||
|
END CASE;
|
||||||
|
end //
|
||||||
|
delimiter ;//
|
||||||
|
# Avoid getting a crashed mysql.proc table.
|
||||||
|
FLUSH TABLES;
|
||||||
|
|
||||||
|
let $numtests = 5;
|
||||||
|
|
||||||
|
let $numinserts = 10;
|
||||||
|
while ($numinserts)
|
||||||
|
{
|
||||||
|
dec $numinserts;
|
||||||
|
INSERT INTO t2(a, b, c, d) VALUES ('a', 'b', 'c', 'd');
|
||||||
|
}
|
||||||
|
|
||||||
|
--enable_reconnect
|
||||||
|
|
||||||
|
while ($numtests)
|
||||||
|
{
|
||||||
|
RESET MASTER;
|
||||||
|
|
||||||
|
START TRANSACTION;
|
||||||
|
insert into t1 select * from t2;
|
||||||
|
# Write file to make mysql-test-run.pl expect crash
|
||||||
|
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
||||||
|
|
||||||
|
eval call setcrash($numtests);
|
||||||
|
|
||||||
|
# Run the crashing query
|
||||||
|
--error 2006,2013
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
# Poll the server waiting for it to be back online again.
|
||||||
|
--source include/wait_until_connected_again.inc
|
||||||
|
|
||||||
|
# table and binlog should be in sync.
|
||||||
|
SELECT * FROM t1 ORDER BY id;
|
||||||
|
SHOW BINLOG EVENTS LIMIT 2,1;
|
||||||
|
|
||||||
|
delete from t1;
|
||||||
|
|
||||||
|
dec $numtests;
|
||||||
|
}
|
||||||
|
|
||||||
|
# final cleanup
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP PROCEDURE setcrash;
|
||||||
|
--disable_query_log
|
||||||
|
eval SET GLOBAL innodb_file_format_check=$file_format_check;
|
||||||
|
--enable_query_log
|
107
sql/handler.cc
107
sql/handler.cc
@@ -78,6 +78,8 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
|
|||||||
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
|
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
|
||||||
uint known_extensions_id= 0;
|
uint known_extensions_id= 0;
|
||||||
|
|
||||||
|
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
|
||||||
|
bool is_real_trans);
|
||||||
|
|
||||||
|
|
||||||
static plugin_ref ha_default_plugin(THD *thd)
|
static plugin_ref ha_default_plugin(THD *thd)
|
||||||
@@ -1076,7 +1078,7 @@ ha_check_and_coalesce_trx_read_only(THD *thd, Ha_trx_info *ha_list,
|
|||||||
*/
|
*/
|
||||||
int ha_commit_trans(THD *thd, bool all)
|
int ha_commit_trans(THD *thd, bool all)
|
||||||
{
|
{
|
||||||
int error= 0, cookie= 0;
|
int error= 0, cookie;
|
||||||
/*
|
/*
|
||||||
'all' means that this is either an explicit commit issued by
|
'all' means that this is either an explicit commit issued by
|
||||||
user, or an implicit commit issued by a DDL.
|
user, or an implicit commit issued by a DDL.
|
||||||
@@ -1091,7 +1093,8 @@ int ha_commit_trans(THD *thd, bool all)
|
|||||||
*/
|
*/
|
||||||
bool is_real_trans= all || thd->transaction.all.ha_list == 0;
|
bool is_real_trans= all || thd->transaction.all.ha_list == 0;
|
||||||
Ha_trx_info *ha_info= trans->ha_list;
|
Ha_trx_info *ha_info= trans->ha_list;
|
||||||
my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
|
bool need_prepare_ordered, need_commit_ordered;
|
||||||
|
my_xid xid;
|
||||||
DBUG_ENTER("ha_commit_trans");
|
DBUG_ENTER("ha_commit_trans");
|
||||||
|
|
||||||
/* Just a random warning to test warnings pushed during autocommit. */
|
/* Just a random warning to test warnings pushed during autocommit. */
|
||||||
@@ -1130,10 +1133,13 @@ int ha_commit_trans(THD *thd, bool all)
|
|||||||
DBUG_RETURN(2);
|
DBUG_RETURN(2);
|
||||||
}
|
}
|
||||||
#ifdef USING_TRANSACTIONS
|
#ifdef USING_TRANSACTIONS
|
||||||
if (ha_info)
|
if (!ha_info)
|
||||||
{
|
{
|
||||||
uint rw_ha_count;
|
/* Free resources and perform other cleanup even for 'empty' transactions. */
|
||||||
bool rw_trans;
|
if (is_real_trans)
|
||||||
|
thd->transaction.cleanup();
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
|
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
|
||||||
|
|
||||||
@@ -1141,9 +1147,9 @@ int ha_commit_trans(THD *thd, bool all)
|
|||||||
if (is_real_trans) /* not a statement commit */
|
if (is_real_trans) /* not a statement commit */
|
||||||
thd->stmt_map.close_transient_cursors();
|
thd->stmt_map.close_transient_cursors();
|
||||||
|
|
||||||
rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
|
uint rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
|
||||||
/* rw_trans is TRUE when we in a transaction changing data */
|
/* rw_trans is TRUE when we in a transaction changing data */
|
||||||
rw_trans= is_real_trans && (rw_ha_count > 0);
|
bool rw_trans= is_real_trans && (rw_ha_count > 0);
|
||||||
|
|
||||||
if (rw_trans &&
|
if (rw_trans &&
|
||||||
wait_if_global_read_lock(thd, 0, 0))
|
wait_if_global_read_lock(thd, 0, 0))
|
||||||
@@ -1158,57 +1164,81 @@ int ha_commit_trans(THD *thd, bool all)
|
|||||||
!thd->slave_thread)
|
!thd->slave_thread)
|
||||||
{
|
{
|
||||||
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
||||||
ha_rollback_trans(thd, all);
|
goto err;
|
||||||
error= 1;
|
}
|
||||||
|
|
||||||
|
if (trans->no_2pc || (rw_ha_count <= 1))
|
||||||
|
{
|
||||||
|
error= ha_commit_one_phase(thd, all);
|
||||||
|
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trans->no_2pc && (rw_ha_count > 1))
|
need_prepare_ordered= FALSE;
|
||||||
{
|
need_commit_ordered= FALSE;
|
||||||
for (; ha_info && !error; ha_info= ha_info->next())
|
xid= thd->transaction.xid_state.xid.get_my_xid();
|
||||||
|
|
||||||
|
for (Ha_trx_info *hi= ha_info; hi; hi= hi->next())
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
handlerton *ht= ha_info->ht();
|
handlerton *ht= hi->ht();
|
||||||
/*
|
/*
|
||||||
Do not call two-phase commit if this particular
|
Do not call two-phase commit if this particular
|
||||||
transaction is read-only. This allows for simpler
|
transaction is read-only. This allows for simpler
|
||||||
implementation in engines that are always read-only.
|
implementation in engines that are always read-only.
|
||||||
*/
|
*/
|
||||||
if (! ha_info->is_trx_read_write())
|
if (! hi->is_trx_read_write())
|
||||||
continue;
|
continue;
|
||||||
/*
|
/*
|
||||||
Sic: we know that prepare() is not NULL since otherwise
|
Sic: we know that prepare() is not NULL since otherwise
|
||||||
trans->no_2pc would have been set.
|
trans->no_2pc would have been set.
|
||||||
*/
|
*/
|
||||||
if ((err= ht->prepare(ht, thd, all)))
|
if ((err= ht->prepare(ht, thd, all)))
|
||||||
{
|
|
||||||
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
|
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
|
||||||
error= 1;
|
|
||||||
}
|
|
||||||
status_var_increment(thd->status_var.ha_prepare_count);
|
status_var_increment(thd->status_var.ha_prepare_count);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (ht->prepare_ordered)
|
||||||
|
need_prepare_ordered= TRUE;
|
||||||
|
if (ht->commit_ordered)
|
||||||
|
need_commit_ordered= TRUE;
|
||||||
}
|
}
|
||||||
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
|
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
|
||||||
if (error || (is_real_trans && xid &&
|
|
||||||
(error= !(cookie= tc_log->log_xid(thd, xid)))))
|
if (!is_real_trans)
|
||||||
{
|
{
|
||||||
ha_rollback_trans(thd, all);
|
error= commit_one_phase_2(thd, all, trans, is_real_trans);
|
||||||
error= 1;
|
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
|
||||||
|
need_commit_ordered);
|
||||||
|
if (!cookie)
|
||||||
|
goto err;
|
||||||
|
|
||||||
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
|
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
|
||||||
}
|
|
||||||
error=ha_commit_one_phase(thd, all) ? (cookie ? 2 : 1) : 0;
|
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
|
||||||
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
|
|
||||||
if (cookie)
|
|
||||||
tc_log->unlog(cookie, xid);
|
|
||||||
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
|
||||||
|
tc_log->unlog(cookie, xid);
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Come here if error and we need to rollback. */
|
||||||
|
err:
|
||||||
|
if (!error)
|
||||||
|
error= 1;
|
||||||
|
ha_rollback_trans(thd, all);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (rw_trans)
|
if (rw_trans)
|
||||||
start_waiting_global_read_lock(thd);
|
start_waiting_global_read_lock(thd);
|
||||||
}
|
|
||||||
/* Free resources and perform other cleanup even for 'empty' transactions. */
|
|
||||||
else if (is_real_trans)
|
|
||||||
thd->transaction.cleanup();
|
|
||||||
#endif /* USING_TRANSACTIONS */
|
#endif /* USING_TRANSACTIONS */
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
@@ -1219,7 +1249,6 @@ end:
|
|||||||
*/
|
*/
|
||||||
int ha_commit_one_phase(THD *thd, bool all)
|
int ha_commit_one_phase(THD *thd, bool all)
|
||||||
{
|
{
|
||||||
int error=0;
|
|
||||||
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
|
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
|
||||||
/*
|
/*
|
||||||
"real" is a nick name for a transaction for which a commit will
|
"real" is a nick name for a transaction for which a commit will
|
||||||
@@ -1229,8 +1258,16 @@ int ha_commit_one_phase(THD *thd, bool all)
|
|||||||
enclosing 'all' transaction is rolled back.
|
enclosing 'all' transaction is rolled back.
|
||||||
*/
|
*/
|
||||||
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
|
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
|
||||||
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
|
|
||||||
DBUG_ENTER("ha_commit_one_phase");
|
DBUG_ENTER("ha_commit_one_phase");
|
||||||
|
DBUG_RETURN(commit_one_phase_2(thd, all, trans, is_real_trans));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
|
||||||
|
{
|
||||||
|
int error= 0;
|
||||||
|
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
|
||||||
|
DBUG_ENTER("commit_one_phase_2");
|
||||||
#ifdef USING_TRANSACTIONS
|
#ifdef USING_TRANSACTIONS
|
||||||
if (ha_info)
|
if (ha_info)
|
||||||
{
|
{
|
||||||
@@ -1847,7 +1884,13 @@ int ha_start_consistent_snapshot(THD *thd)
|
|||||||
{
|
{
|
||||||
bool warn= true;
|
bool warn= true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Holding the LOCK_commit_ordered mutex ensures that for any transaction
|
||||||
|
we either see it committed in all engines, or in none.
|
||||||
|
*/
|
||||||
|
pthread_mutex_lock(&LOCK_commit_ordered);
|
||||||
plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);
|
plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);
|
||||||
|
pthread_mutex_unlock(&LOCK_commit_ordered);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Same idea as when one wants to CREATE TABLE in one engine which does not
|
Same idea as when one wants to CREATE TABLE in one engine which does not
|
||||||
|
@@ -765,8 +765,97 @@ struct handlerton
|
|||||||
and 'real commit' mean the same event.
|
and 'real commit' mean the same event.
|
||||||
*/
|
*/
|
||||||
int (*commit)(handlerton *hton, THD *thd, bool all);
|
int (*commit)(handlerton *hton, THD *thd, bool all);
|
||||||
|
/*
|
||||||
|
The commit_ordered() method is called prior to the commit() method, after
|
||||||
|
the transaction manager has decided to commit (not rollback) the
|
||||||
|
transaction. Unlike commit(), commit_ordered() is called only when the
|
||||||
|
full transaction is committed, not for each commit of statement
|
||||||
|
transaction in a multi-statement transaction.
|
||||||
|
|
||||||
|
Not that like prepare(), commit_ordered() is only called when 2-phase
|
||||||
|
commit takes place. Ie. when no binary log and only a single engine
|
||||||
|
participates in a transaction, one commit() is called, no
|
||||||
|
commit_orderd(). So engines must be prepared for this.
|
||||||
|
|
||||||
|
The calls to commit_ordered() in multiple parallel transactions is
|
||||||
|
guaranteed to happen in the same order in every participating
|
||||||
|
handler. This can be used to ensure the same commit order among multiple
|
||||||
|
handlers (eg. in table handler and binlog). So if transaction T1 calls
|
||||||
|
into commit_ordered() of handler A before T2, then T1 will also call
|
||||||
|
commit_ordered() of handler B before T2.
|
||||||
|
|
||||||
|
Engines that implement this method should during this call make the
|
||||||
|
transaction visible to other transactions, thereby making the order of
|
||||||
|
transaction commits be defined by the order of commit_ordered() calls.
|
||||||
|
|
||||||
|
The intension is that commit_ordered() should do the minimal amount of
|
||||||
|
work that needs to happen in consistent commit order among handlers. To
|
||||||
|
preserve ordering, calls need to be serialised on a global mutex, so
|
||||||
|
doing any time-consuming or blocking operations in commit_ordered() will
|
||||||
|
limit scalability.
|
||||||
|
|
||||||
|
Handlers can rely on commit_ordered() calls to be serialised (no two
|
||||||
|
calls can run in parallel, so no extra locking on the handler part is
|
||||||
|
required to ensure this).
|
||||||
|
|
||||||
|
Note that commit_ordered() can be called from a different thread than the
|
||||||
|
one handling the transaction! So it can not do anything that depends on
|
||||||
|
thread local storage, in particular it can not call my_error() and
|
||||||
|
friends (instead it can store the error code and delay the call of
|
||||||
|
my_error() to the commit() method).
|
||||||
|
|
||||||
|
Similarly, since commit_ordered() returns void, any return error code
|
||||||
|
must be saved and returned from the commit() method instead.
|
||||||
|
|
||||||
|
The commit_ordered method is optional, and can be left unset if not
|
||||||
|
needed in a particular handler (then there will be no ordering guarantees
|
||||||
|
wrt. other engines and binary log).
|
||||||
|
*/
|
||||||
|
void (*commit_ordered)(handlerton *hton, THD *thd, bool all);
|
||||||
int (*rollback)(handlerton *hton, THD *thd, bool all);
|
int (*rollback)(handlerton *hton, THD *thd, bool all);
|
||||||
int (*prepare)(handlerton *hton, THD *thd, bool all);
|
int (*prepare)(handlerton *hton, THD *thd, bool all);
|
||||||
|
/*
|
||||||
|
The prepare_ordered method is optional. If set, it will be called after
|
||||||
|
successful prepare() in all handlers participating in 2-phase
|
||||||
|
commit. Like commit_ordered(), it is called only when the full
|
||||||
|
transaction is committed, not for each commit of statement transaction.
|
||||||
|
|
||||||
|
The calls to prepare_ordered() among multiple parallel transactions are
|
||||||
|
ordered consistently with calls to commit_ordered(). This means that
|
||||||
|
calls to prepare_ordered() effectively define the commit order, and that
|
||||||
|
each handler will see the same sequence of transactions calling into
|
||||||
|
prepare_ordered() and commit_ordered().
|
||||||
|
|
||||||
|
Thus, prepare_ordered() can be used to define commit order for handlers
|
||||||
|
that need to do this in the prepare step (like binlog). It can also be
|
||||||
|
used to release transaction's locks early in an order consistent with the
|
||||||
|
order transactions will be eventually committed.
|
||||||
|
|
||||||
|
Like commit_ordered(), prepare_ordered() calls are serialised to maintain
|
||||||
|
ordering, so the intension is that they should execute fast, with only
|
||||||
|
the minimal amount of work needed to define commit order. Handlers can
|
||||||
|
rely on this serialisation, and do not need to do any extra locking to
|
||||||
|
avoid two prepare_ordered() calls running in parallel.
|
||||||
|
|
||||||
|
Like commit_ordered(), prepare_ordered() is not guaranteed to be called
|
||||||
|
in the context of the thread handling the rest of the transaction. So it
|
||||||
|
cannot invoke code that relies on thread local storage, in particular it
|
||||||
|
cannot call my_error().
|
||||||
|
|
||||||
|
prepare_ordered() cannot cause a rollback by returning an error, all
|
||||||
|
possible errors must be handled in prepare() (the prepare_ordered()
|
||||||
|
method returns void). In case of some fatal error, a record of the error
|
||||||
|
must be made internally by the engine and returned from commit() later.
|
||||||
|
|
||||||
|
Note that for user-level XA SQL commands, no consistent ordering among
|
||||||
|
prepare_ordered() and commit_ordered() is guaranteed (as that would
|
||||||
|
require blocking all other commits for an indefinite time).
|
||||||
|
|
||||||
|
When 2-phase commit is not used (eg. only one engine (and no binlog) in
|
||||||
|
transaction), prepare() is not called and in such cases prepare_ordered()
|
||||||
|
also is not called.
|
||||||
|
*/
|
||||||
|
void (*prepare_ordered)(handlerton *hton, THD *thd, bool all);
|
||||||
int (*recover)(handlerton *hton, XID *xid_list, uint len);
|
int (*recover)(handlerton *hton, XID *xid_list, uint len);
|
||||||
int (*commit_by_xid)(handlerton *hton, XID *xid);
|
int (*commit_by_xid)(handlerton *hton, XID *xid);
|
||||||
int (*rollback_by_xid)(handlerton *hton, XID *xid);
|
int (*rollback_by_xid)(handlerton *hton, XID *xid);
|
||||||
|
925
sql/log.cc
925
sql/log.cc
File diff suppressed because it is too large
Load Diff
122
sql/log.h
122
sql/log.h
@@ -38,17 +38,58 @@ class TC_LOG
|
|||||||
|
|
||||||
virtual int open(const char *opt_name)=0;
|
virtual int open(const char *opt_name)=0;
|
||||||
virtual void close()=0;
|
virtual void close()=0;
|
||||||
virtual int log_xid(THD *thd, my_xid xid)=0;
|
virtual int log_and_order(THD *thd, my_xid xid, bool all,
|
||||||
|
bool need_prepare_ordered,
|
||||||
|
bool need_commit_ordered) = 0;
|
||||||
virtual void unlog(ulong cookie, my_xid xid)=0;
|
virtual void unlog(ulong cookie, my_xid xid)=0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
These methods are meant to be invoked from log_and_order() implementations
|
||||||
|
to run any prepare_ordered() respectively commit_ordered() methods in
|
||||||
|
participating handlers.
|
||||||
|
|
||||||
|
They must be called using suitable thread syncronisation to ensure that
|
||||||
|
they are each called in the correct commit order among all
|
||||||
|
transactions. However, it is only necessary to call them if the
|
||||||
|
corresponding flag passed to log_and_order is set (it is safe, but not
|
||||||
|
required, to call them when the flag is false).
|
||||||
|
|
||||||
|
The caller must be holding LOCK_prepare_ordered respectively
|
||||||
|
LOCK_commit_ordered when calling these methods.
|
||||||
|
*/
|
||||||
|
void run_prepare_ordered(THD *thd, bool all);
|
||||||
|
void run_commit_ordered(THD *thd, bool all);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Locks used to ensure serialised execution of TC_LOG::run_prepare_ordered()
|
||||||
|
and TC_LOG::run_commit_ordered(), or any other code that calls handler
|
||||||
|
prepare_ordered() or commit_ordered() methods.
|
||||||
|
*/
|
||||||
|
extern pthread_mutex_t LOCK_prepare_ordered;
|
||||||
|
extern pthread_mutex_t LOCK_commit_ordered;
|
||||||
|
|
||||||
|
extern void TC_init();
|
||||||
|
extern void TC_destroy();
|
||||||
|
|
||||||
class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
|
class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TC_LOG_DUMMY() {}
|
TC_LOG_DUMMY() {}
|
||||||
int open(const char *opt_name) { return 0; }
|
int open(const char *opt_name) { return 0; }
|
||||||
void close() { }
|
void close() { }
|
||||||
int log_xid(THD *thd, my_xid xid) { return 1; }
|
/*
|
||||||
|
TC_LOG_DUMMY is only used when there are <= 1 XA-capable engines, and we
|
||||||
|
only use internal XA during commit when >= 2 XA-capable engines
|
||||||
|
participate.
|
||||||
|
*/
|
||||||
|
int log_and_order(THD *thd, my_xid xid, bool all,
|
||||||
|
bool need_prepare_ordered, bool need_commit_ordered)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(0 /* Internal error - TC_LOG_DUMMY::log_and_order() called */);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
void unlog(ulong cookie, my_xid xid) { }
|
void unlog(ulong cookie, my_xid xid) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -74,6 +115,13 @@ class TC_LOG_MMAP: public TC_LOG
|
|||||||
pthread_cond_t cond; // to wait for a sync
|
pthread_cond_t cond; // to wait for a sync
|
||||||
} PAGE;
|
} PAGE;
|
||||||
|
|
||||||
|
/* List of THDs for which to invoke commit_ordered(), in order. */
|
||||||
|
struct commit_entry
|
||||||
|
{
|
||||||
|
struct commit_entry *next;
|
||||||
|
THD *thd;
|
||||||
|
};
|
||||||
|
|
||||||
char logname[FN_REFLEN];
|
char logname[FN_REFLEN];
|
||||||
File fd;
|
File fd;
|
||||||
my_off_t file_length;
|
my_off_t file_length;
|
||||||
@@ -88,16 +136,38 @@ class TC_LOG_MMAP: public TC_LOG
|
|||||||
*/
|
*/
|
||||||
pthread_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
|
pthread_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
|
||||||
pthread_cond_t COND_pool, COND_active;
|
pthread_cond_t COND_pool, COND_active;
|
||||||
|
/*
|
||||||
|
Queue of threads that need to call commit_ordered().
|
||||||
|
Access to this queue must be protected by LOCK_prepare_ordered.
|
||||||
|
*/
|
||||||
|
commit_entry *commit_ordered_queue;
|
||||||
|
/*
|
||||||
|
This flag and condition is used to reserve the queue while threads in it
|
||||||
|
each run the commit_ordered() methods one after the other. Only once the
|
||||||
|
last commit_ordered() in the queue is done can we start on a new queue
|
||||||
|
run.
|
||||||
|
|
||||||
|
Since we start this process in the first thread in the queue and finish in
|
||||||
|
the last (and possibly different) thread, we need a condition variable for
|
||||||
|
this (we cannot unlock a mutex in a different thread than the one who
|
||||||
|
locked it).
|
||||||
|
|
||||||
|
The condition is used together with the LOCK_prepare_ordered mutex.
|
||||||
|
*/
|
||||||
|
my_bool commit_ordered_queue_busy;
|
||||||
|
pthread_cond_t COND_queue_busy;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TC_LOG_MMAP(): inited(0) {}
|
TC_LOG_MMAP(): inited(0) {}
|
||||||
int open(const char *opt_name);
|
int open(const char *opt_name);
|
||||||
void close();
|
void close();
|
||||||
int log_xid(THD *thd, my_xid xid);
|
int log_and_order(THD *thd, my_xid xid, bool all,
|
||||||
|
bool need_prepare_ordered, bool need_commit_ordered);
|
||||||
void unlog(ulong cookie, my_xid xid);
|
void unlog(ulong cookie, my_xid xid);
|
||||||
int recover();
|
int recover();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int log_one_transaction(my_xid xid);
|
||||||
void get_active_from_pool();
|
void get_active_from_pool();
|
||||||
int sync();
|
int sync();
|
||||||
int overflow();
|
int overflow();
|
||||||
@@ -232,9 +302,31 @@ private:
|
|||||||
time_t last_time;
|
time_t last_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class binlog_trx_data;
|
||||||
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
struct group_commit_entry
|
||||||
|
{
|
||||||
|
struct group_commit_entry *next;
|
||||||
|
THD *thd;
|
||||||
|
binlog_trx_data *trx_data;
|
||||||
|
/*
|
||||||
|
Extra events (BEGIN, COMMIT/ROLLBACK/XID, and possibly INCIDENT) to be
|
||||||
|
written during group commit. The incident_event is only valid if
|
||||||
|
trx_data->has_incident() is true.
|
||||||
|
*/
|
||||||
|
Log_event *begin_event;
|
||||||
|
Log_event *end_event;
|
||||||
|
Log_event *incident_event;
|
||||||
|
/* Set during group commit to record any per-thread error. */
|
||||||
|
int error;
|
||||||
|
int commit_errno;
|
||||||
|
/* This is the `all' parameter for ha_commit_ordered(). */
|
||||||
|
bool all;
|
||||||
|
/* True if we come in through XA log_and_order(), false otherwise. */
|
||||||
|
};
|
||||||
|
|
||||||
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
|
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
|
||||||
pthread_mutex_t LOCK_index;
|
pthread_mutex_t LOCK_index;
|
||||||
pthread_mutex_t LOCK_prep_xids;
|
pthread_mutex_t LOCK_prep_xids;
|
||||||
@@ -276,6 +368,12 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
|||||||
In 5.0 it's 0 for relay logs too!
|
In 5.0 it's 0 for relay logs too!
|
||||||
*/
|
*/
|
||||||
bool no_auto_events;
|
bool no_auto_events;
|
||||||
|
/* Queue of transactions queued up to participate in group commit. */
|
||||||
|
group_commit_entry *group_commit_queue;
|
||||||
|
/* Total number of committed transactions. */
|
||||||
|
ulonglong num_commits;
|
||||||
|
/* Number of group commits done. */
|
||||||
|
ulonglong num_group_commits;
|
||||||
|
|
||||||
int write_to_file(IO_CACHE *cache);
|
int write_to_file(IO_CACHE *cache);
|
||||||
/*
|
/*
|
||||||
@@ -285,6 +383,11 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
|||||||
*/
|
*/
|
||||||
void new_file_without_locking();
|
void new_file_without_locking();
|
||||||
void new_file_impl(bool need_lock);
|
void new_file_impl(bool need_lock);
|
||||||
|
int write_transaction(group_commit_entry *entry);
|
||||||
|
bool write_transaction_to_binlog_events(group_commit_entry *entry);
|
||||||
|
void trx_group_commit_leader(group_commit_entry *leader);
|
||||||
|
void mark_xid_done();
|
||||||
|
void mark_xids_active(uint xid_count);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MYSQL_LOG::generate_name;
|
MYSQL_LOG::generate_name;
|
||||||
@@ -313,7 +416,8 @@ public:
|
|||||||
|
|
||||||
int open(const char *opt_name);
|
int open(const char *opt_name);
|
||||||
void close();
|
void close();
|
||||||
int log_xid(THD *thd, my_xid xid);
|
int log_and_order(THD *thd, my_xid xid, bool all,
|
||||||
|
bool need_prepare_ordered, bool need_commit_ordered);
|
||||||
void unlog(ulong cookie, my_xid xid);
|
void unlog(ulong cookie, my_xid xid);
|
||||||
int recover(IO_CACHE *log, Format_description_log_event *fdle);
|
int recover(IO_CACHE *log, Format_description_log_event *fdle);
|
||||||
#if !defined(MYSQL_CLIENT)
|
#if !defined(MYSQL_CLIENT)
|
||||||
@@ -358,10 +462,11 @@ public:
|
|||||||
|
|
||||||
void reset_gathered_updates(THD *thd);
|
void reset_gathered_updates(THD *thd);
|
||||||
bool write(Log_event* event_info); // binary log write
|
bool write(Log_event* event_info); // binary log write
|
||||||
bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
|
bool write_transaction_to_binlog(THD *thd, binlog_trx_data *trx_data,
|
||||||
bool write_incident(THD *thd, bool lock);
|
Log_event *end_ev, bool all);
|
||||||
int write_cache(THD *thd, IO_CACHE *cache, bool lock_log,
|
|
||||||
bool flush_and_sync);
|
bool write_incident(THD *thd);
|
||||||
|
int write_cache(THD *thd, IO_CACHE *cache);
|
||||||
void set_write_error(THD *thd);
|
void set_write_error(THD *thd);
|
||||||
bool check_write_error(THD *thd);
|
bool check_write_error(THD *thd);
|
||||||
|
|
||||||
@@ -416,6 +521,7 @@ public:
|
|||||||
inline void unlock_index() { pthread_mutex_unlock(&LOCK_index);}
|
inline void unlock_index() { pthread_mutex_unlock(&LOCK_index);}
|
||||||
inline IO_CACHE *get_index_file() { return &index_file;}
|
inline IO_CACHE *get_index_file() { return &index_file;}
|
||||||
inline uint32 get_open_count() { return open_count; }
|
inline uint32 get_open_count() { return open_count; }
|
||||||
|
void set_status_variables();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Log_event_handler
|
class Log_event_handler
|
||||||
|
@@ -1391,6 +1391,7 @@ void clean_up(bool print_message)
|
|||||||
ha_end();
|
ha_end();
|
||||||
if (tc_log)
|
if (tc_log)
|
||||||
tc_log->close();
|
tc_log->close();
|
||||||
|
TC_destroy();
|
||||||
xid_cache_free();
|
xid_cache_free();
|
||||||
wt_end();
|
wt_end();
|
||||||
delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache);
|
delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache);
|
||||||
@@ -4241,6 +4242,8 @@ a file name for --log-bin-index option", opt_binlog_index_name);
|
|||||||
if (!errmesg[0][0])
|
if (!errmesg[0][0])
|
||||||
unireg_abort(1);
|
unireg_abort(1);
|
||||||
|
|
||||||
|
TC_init();
|
||||||
|
|
||||||
/* We have to initialize the storage engines before CSV logging */
|
/* We have to initialize the storage engines before CSV logging */
|
||||||
if (ha_init())
|
if (ha_init())
|
||||||
{
|
{
|
||||||
|
@@ -761,6 +761,8 @@ THD::THD()
|
|||||||
active_vio = 0;
|
active_vio = 0;
|
||||||
#endif
|
#endif
|
||||||
pthread_mutex_init(&LOCK_thd_data, MY_MUTEX_INIT_FAST);
|
pthread_mutex_init(&LOCK_thd_data, MY_MUTEX_INIT_FAST);
|
||||||
|
pthread_mutex_init(&LOCK_wakeup_ready, MY_MUTEX_INIT_FAST);
|
||||||
|
pthread_cond_init(&COND_wakeup_ready, 0);
|
||||||
|
|
||||||
/* Variables with default values */
|
/* Variables with default values */
|
||||||
proc_info="login";
|
proc_info="login";
|
||||||
@@ -1147,6 +1149,8 @@ THD::~THD()
|
|||||||
free_root(&transaction.mem_root,MYF(0));
|
free_root(&transaction.mem_root,MYF(0));
|
||||||
#endif
|
#endif
|
||||||
mysys_var=0; // Safety (shouldn't be needed)
|
mysys_var=0; // Safety (shouldn't be needed)
|
||||||
|
pthread_cond_destroy(&COND_wakeup_ready);
|
||||||
|
pthread_mutex_destroy(&LOCK_wakeup_ready);
|
||||||
pthread_mutex_destroy(&LOCK_thd_data);
|
pthread_mutex_destroy(&LOCK_thd_data);
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
dbug_sentry= THD_SENTRY_GONE;
|
dbug_sentry= THD_SENTRY_GONE;
|
||||||
@@ -4150,6 +4154,25 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
THD::wait_for_wakeup_ready()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_wakeup_ready);
|
||||||
|
while (!wakeup_ready)
|
||||||
|
pthread_cond_wait(&COND_wakeup_ready, &LOCK_wakeup_ready);
|
||||||
|
pthread_mutex_unlock(&LOCK_wakeup_ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
THD::signal_wakeup_ready()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_wakeup_ready);
|
||||||
|
wakeup_ready= true;
|
||||||
|
pthread_cond_signal(&COND_wakeup_ready);
|
||||||
|
pthread_mutex_unlock(&LOCK_wakeup_ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
|
bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
|
||||||
ulonglong incr)
|
ulonglong incr)
|
||||||
{
|
{
|
||||||
|
@@ -2454,6 +2454,14 @@ public:
|
|||||||
return backup;
|
return backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear_wakeup_ready() { wakeup_ready= false; }
|
||||||
|
/*
|
||||||
|
Sleep waiting for others to wake us up with signal_wakeup_ready().
|
||||||
|
Must call clear_wakeup_ready() before waiting.
|
||||||
|
*/
|
||||||
|
void wait_for_wakeup_ready();
|
||||||
|
/* Wake this thread up from wait_for_wakeup_ready(). */
|
||||||
|
void signal_wakeup_ready();
|
||||||
private:
|
private:
|
||||||
/** The current internal error handler for this thread, or NULL. */
|
/** The current internal error handler for this thread, or NULL. */
|
||||||
Internal_error_handler *m_internal_handler;
|
Internal_error_handler *m_internal_handler;
|
||||||
@@ -2492,6 +2500,16 @@ private:
|
|||||||
*/
|
*/
|
||||||
LEX_STRING invoker_user;
|
LEX_STRING invoker_user;
|
||||||
LEX_STRING invoker_host;
|
LEX_STRING invoker_host;
|
||||||
|
/*
|
||||||
|
Flag, mutex and condition for a thread to wait for a signal from another
|
||||||
|
thread.
|
||||||
|
|
||||||
|
Currently used to wait for group commit to complete, can also be used for
|
||||||
|
other purposes.
|
||||||
|
*/
|
||||||
|
bool wakeup_ready;
|
||||||
|
pthread_mutex_t LOCK_wakeup_ready;
|
||||||
|
pthread_cond_t COND_wakeup_ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A short cut for thd->main_da.set_ok_status(). */
|
/** A short cut for thd->main_da.set_ok_status(). */
|
||||||
|
@@ -1007,6 +1007,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
DBUG_ENTER("dispatch_command");
|
DBUG_ENTER("dispatch_command");
|
||||||
DBUG_PRINT("info", ("command: %d", command));
|
DBUG_PRINT("info", ("command: %d", command));
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("crash_dispatch_command_before",
|
||||||
|
{ DBUG_PRINT("crash_dispatch_command_before", ("now"));
|
||||||
|
DBUG_ABORT(); });
|
||||||
|
|
||||||
thd->command=command;
|
thd->command=command;
|
||||||
/*
|
/*
|
||||||
Commands which always take a long time are logged into
|
Commands which always take a long time are logged into
|
||||||
|
@@ -117,8 +117,6 @@ bool check_global_access(THD *thd, ulong want_access);
|
|||||||
|
|
||||||
/** to protect innobase_open_files */
|
/** to protect innobase_open_files */
|
||||||
static pthread_mutex_t innobase_share_mutex;
|
static pthread_mutex_t innobase_share_mutex;
|
||||||
/** to force correct commit order in binlog */
|
|
||||||
static pthread_mutex_t prepare_commit_mutex;
|
|
||||||
static ulong commit_threads = 0;
|
static ulong commit_threads = 0;
|
||||||
static pthread_mutex_t commit_threads_m;
|
static pthread_mutex_t commit_threads_m;
|
||||||
static pthread_cond_t commit_cond;
|
static pthread_cond_t commit_cond;
|
||||||
@@ -222,6 +220,7 @@ static const char* innobase_change_buffering_values[IBUF_USE_COUNT] = {
|
|||||||
static INNOBASE_SHARE *get_share(const char *table_name);
|
static INNOBASE_SHARE *get_share(const char *table_name);
|
||||||
static void free_share(INNOBASE_SHARE *share);
|
static void free_share(INNOBASE_SHARE *share);
|
||||||
static int innobase_close_connection(handlerton *hton, THD* thd);
|
static int innobase_close_connection(handlerton *hton, THD* thd);
|
||||||
|
static void innobase_commit_ordered(handlerton *hton, THD* thd, bool all);
|
||||||
static int innobase_commit(handlerton *hton, THD* thd, bool all);
|
static int innobase_commit(handlerton *hton, THD* thd, bool all);
|
||||||
static int innobase_rollback(handlerton *hton, THD* thd, bool all);
|
static int innobase_rollback(handlerton *hton, THD* thd, bool all);
|
||||||
static int innobase_rollback_to_savepoint(handlerton *hton, THD* thd,
|
static int innobase_rollback_to_savepoint(handlerton *hton, THD* thd,
|
||||||
@@ -1365,7 +1364,6 @@ innobase_trx_init(
|
|||||||
trx_t* trx) /*!< in/out: InnoDB transaction handle */
|
trx_t* trx) /*!< in/out: InnoDB transaction handle */
|
||||||
{
|
{
|
||||||
DBUG_ENTER("innobase_trx_init");
|
DBUG_ENTER("innobase_trx_init");
|
||||||
DBUG_ASSERT(EQ_CURRENT_THD(thd));
|
|
||||||
DBUG_ASSERT(thd == trx->mysql_thd);
|
DBUG_ASSERT(thd == trx->mysql_thd);
|
||||||
|
|
||||||
trx->check_foreigns = !thd_test_options(
|
trx->check_foreigns = !thd_test_options(
|
||||||
@@ -1424,8 +1422,6 @@ check_trx_exists(
|
|||||||
{
|
{
|
||||||
trx_t*& trx = thd_to_trx(thd);
|
trx_t*& trx = thd_to_trx(thd);
|
||||||
|
|
||||||
ut_ad(EQ_CURRENT_THD(thd));
|
|
||||||
|
|
||||||
if (trx == NULL) {
|
if (trx == NULL) {
|
||||||
trx = innobase_trx_allocate(thd);
|
trx = innobase_trx_allocate(thd);
|
||||||
} else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) {
|
} else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) {
|
||||||
@@ -1717,10 +1713,10 @@ innobase_query_caching_of_table_permitted(
|
|||||||
/* The call of row_search_.. will start a new transaction if it is
|
/* The call of row_search_.. will start a new transaction if it is
|
||||||
not yet started */
|
not yet started */
|
||||||
|
|
||||||
if (trx->active_trans == 0) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0) {
|
||||||
|
|
||||||
innobase_register_trx_and_stmt(innodb_hton_ptr, thd);
|
innobase_register_trx_and_stmt(innodb_hton_ptr, thd);
|
||||||
trx->active_trans = 1;
|
trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row_search_check_if_query_cache_permitted(trx, norm_name)) {
|
if (row_search_check_if_query_cache_permitted(trx, norm_name)) {
|
||||||
@@ -1990,11 +1986,11 @@ ha_innobase::init_table_handle_for_HANDLER(void)
|
|||||||
|
|
||||||
/* Set the MySQL flag to mark that there is an active transaction */
|
/* Set the MySQL flag to mark that there is an active transaction */
|
||||||
|
|
||||||
if (prebuilt->trx->active_trans == 0) {
|
if ((prebuilt->trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0) {
|
||||||
|
|
||||||
innobase_register_trx_and_stmt(ht, user_thd);
|
innobase_register_trx_and_stmt(ht, user_thd);
|
||||||
|
|
||||||
prebuilt->trx->active_trans = 1;
|
prebuilt->trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We did the necessary inits in this function, no need to repeat them
|
/* We did the necessary inits in this function, no need to repeat them
|
||||||
@@ -2045,6 +2041,7 @@ innobase_init(
|
|||||||
innobase_hton->savepoint_set=innobase_savepoint;
|
innobase_hton->savepoint_set=innobase_savepoint;
|
||||||
innobase_hton->savepoint_rollback=innobase_rollback_to_savepoint;
|
innobase_hton->savepoint_rollback=innobase_rollback_to_savepoint;
|
||||||
innobase_hton->savepoint_release=innobase_release_savepoint;
|
innobase_hton->savepoint_release=innobase_release_savepoint;
|
||||||
|
innobase_hton->commit_ordered=innobase_commit_ordered;
|
||||||
innobase_hton->commit=innobase_commit;
|
innobase_hton->commit=innobase_commit;
|
||||||
innobase_hton->rollback=innobase_rollback;
|
innobase_hton->rollback=innobase_rollback;
|
||||||
innobase_hton->prepare=innobase_xa_prepare;
|
innobase_hton->prepare=innobase_xa_prepare;
|
||||||
@@ -2547,7 +2544,6 @@ skip_overwrite:
|
|||||||
|
|
||||||
innobase_open_tables = hash_create(200);
|
innobase_open_tables = hash_create(200);
|
||||||
pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
|
pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
|
||||||
pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
|
|
||||||
pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
|
pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
|
||||||
pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
|
pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
|
||||||
pthread_cond_init(&commit_cond, NULL);
|
pthread_cond_init(&commit_cond, NULL);
|
||||||
@@ -2602,7 +2598,6 @@ innobase_end(
|
|||||||
my_free(internal_innobase_data_file_path,
|
my_free(internal_innobase_data_file_path,
|
||||||
MYF(MY_ALLOW_ZERO_PTR));
|
MYF(MY_ALLOW_ZERO_PTR));
|
||||||
pthread_mutex_destroy(&innobase_share_mutex);
|
pthread_mutex_destroy(&innobase_share_mutex);
|
||||||
pthread_mutex_destroy(&prepare_commit_mutex);
|
|
||||||
pthread_mutex_destroy(&commit_threads_m);
|
pthread_mutex_destroy(&commit_threads_m);
|
||||||
pthread_mutex_destroy(&commit_cond_m);
|
pthread_mutex_destroy(&commit_cond_m);
|
||||||
pthread_cond_destroy(&commit_cond);
|
pthread_cond_destroy(&commit_cond);
|
||||||
@@ -2723,14 +2718,119 @@ innobase_start_trx_and_assign_read_view(
|
|||||||
|
|
||||||
/* Set the MySQL flag to mark that there is an active transaction */
|
/* Set the MySQL flag to mark that there is an active transaction */
|
||||||
|
|
||||||
if (trx->active_trans == 0) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0) {
|
||||||
innobase_register_trx_and_stmt(hton, thd);
|
innobase_register_trx_and_stmt(hton, thd);
|
||||||
trx->active_trans = 1;
|
trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
innobase_commit_ordered_2(
|
||||||
|
/*============*/
|
||||||
|
trx_t* trx, /*!< in: Innodb transaction */
|
||||||
|
THD* thd) /*!< in: MySQL thread handle */
|
||||||
|
{
|
||||||
|
ulonglong tmp_pos;
|
||||||
|
DBUG_ENTER("innobase_commit_ordered");
|
||||||
|
|
||||||
|
/* We need current binlog position for ibbackup to work.
|
||||||
|
Note, the position is current because commit_ordered is guaranteed
|
||||||
|
to be called in same sequenece as writing to binlog. */
|
||||||
|
|
||||||
|
retry:
|
||||||
|
if (innobase_commit_concurrency > 0) {
|
||||||
|
pthread_mutex_lock(&commit_cond_m);
|
||||||
|
commit_threads++;
|
||||||
|
|
||||||
|
if (commit_threads > innobase_commit_concurrency) {
|
||||||
|
commit_threads--;
|
||||||
|
pthread_cond_wait(&commit_cond,
|
||||||
|
&commit_cond_m);
|
||||||
|
pthread_mutex_unlock(&commit_cond_m);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pthread_mutex_unlock(&commit_cond_m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_bin_log_commit_pos(thd, &tmp_pos, &(trx->mysql_log_file_name));
|
||||||
|
trx->mysql_log_offset = (ib_int64_t) tmp_pos;
|
||||||
|
|
||||||
|
/* Don't do write + flush right now. For group commit
|
||||||
|
to work we want to do the flush in the innobase_commit()
|
||||||
|
method, which runs without holding any locks. */
|
||||||
|
trx->flush_log_later = TRUE;
|
||||||
|
innobase_commit_low(trx);
|
||||||
|
trx->flush_log_later = FALSE;
|
||||||
|
|
||||||
|
if (innobase_commit_concurrency > 0) {
|
||||||
|
pthread_mutex_lock(&commit_cond_m);
|
||||||
|
commit_threads--;
|
||||||
|
pthread_cond_signal(&commit_cond);
|
||||||
|
pthread_mutex_unlock(&commit_cond_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************//**
|
||||||
|
Perform the first, fast part of InnoDB commit.
|
||||||
|
|
||||||
|
Doing it in this call ensures that we get the same commit order here
|
||||||
|
as in binlog and any other participating transactional storage engines.
|
||||||
|
|
||||||
|
Note that we want to do as little as really needed here, as we run
|
||||||
|
under a global mutex. The expensive fsync() is done later, in
|
||||||
|
innobase_commit(), without a lock so group commit can take place.
|
||||||
|
|
||||||
|
Note also that this method can be called from a different thread than
|
||||||
|
the one handling the rest of the transaction. */
|
||||||
|
static
|
||||||
|
void
|
||||||
|
innobase_commit_ordered(
|
||||||
|
/*============*/
|
||||||
|
handlerton *hton, /*!< in: Innodb handlerton */
|
||||||
|
THD* thd, /*!< in: MySQL thread handle of the user for whom
|
||||||
|
the transaction should be committed */
|
||||||
|
bool all) /*!< in: TRUE - commit transaction
|
||||||
|
FALSE - the current SQL statement ended */
|
||||||
|
{
|
||||||
|
trx_t* trx;
|
||||||
|
DBUG_ENTER("innobase_commit_ordered");
|
||||||
|
DBUG_ASSERT(hton == innodb_hton_ptr);
|
||||||
|
|
||||||
|
trx = check_trx_exists(thd);
|
||||||
|
|
||||||
|
/* Since we will reserve the kernel mutex, we have to release
|
||||||
|
the search system latch first to obey the latching order. */
|
||||||
|
|
||||||
|
if (trx->has_search_latch) {
|
||||||
|
trx_search_latch_release_if_reserved(trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0
|
||||||
|
&& trx->conc_state != TRX_NOT_STARTED) {
|
||||||
|
/* We cannot throw error here; instead we will catch this error
|
||||||
|
again in innobase_commit() and report it from there. */
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* commit_ordered is only called when committing the whole transaction
|
||||||
|
(or an SQL statement when autocommit is on). */
|
||||||
|
DBUG_ASSERT(all ||
|
||||||
|
(!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)));
|
||||||
|
|
||||||
|
innobase_commit_ordered_2(trx, thd);
|
||||||
|
|
||||||
|
trx->active_trans |= TRX_ACTIVE_COMMIT_ORDERED;
|
||||||
|
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************//**
|
/*****************************************************************//**
|
||||||
Commits a transaction in an InnoDB database or marks an SQL statement
|
Commits a transaction in an InnoDB database or marks an SQL statement
|
||||||
ended.
|
ended.
|
||||||
@@ -2756,11 +2856,12 @@ innobase_commit(
|
|||||||
/* Since we will reserve the kernel mutex, we have to release
|
/* Since we will reserve the kernel mutex, we have to release
|
||||||
the search system latch first to obey the latching order. */
|
the search system latch first to obey the latching order. */
|
||||||
|
|
||||||
if (trx->has_search_latch) {
|
if (trx->has_search_latch &&
|
||||||
|
(trx->active_trans & TRX_ACTIVE_COMMIT_ORDERED) == 0) {
|
||||||
trx_search_latch_release_if_reserved(trx);
|
trx_search_latch_release_if_reserved(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The flag trx->active_trans is set to 1 in
|
/* The flag TRX_ACTIVE_IN_MYSQL in trx->active_trans is set in
|
||||||
|
|
||||||
1. ::external_lock(),
|
1. ::external_lock(),
|
||||||
2. ::start_stmt(),
|
2. ::start_stmt(),
|
||||||
@@ -2770,79 +2871,31 @@ innobase_commit(
|
|||||||
6. innobase_start_trx_and_assign_read_view(),
|
6. innobase_start_trx_and_assign_read_view(),
|
||||||
7. ::transactional_table_lock()
|
7. ::transactional_table_lock()
|
||||||
|
|
||||||
and it is only set to 0 in a commit or a rollback. If it is 0 we know
|
and it is only cleared in a commit or a rollback. If it is unset we know
|
||||||
there cannot be resources to be freed and we could return immediately.
|
there cannot be resources to be freed and we could return immediately.
|
||||||
For the time being, we play safe and do the cleanup though there should
|
For the time being, we play safe and do the cleanup though there should
|
||||||
be nothing to clean up. */
|
be nothing to clean up. */
|
||||||
|
|
||||||
if (trx->active_trans == 0
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0
|
||||||
&& trx->conc_state != TRX_NOT_STARTED) {
|
&& trx->conc_state != TRX_NOT_STARTED) {
|
||||||
|
|
||||||
sql_print_error("trx->active_trans == 0, but"
|
sql_print_error("trx->active_trans == 0, but"
|
||||||
" trx->conc_state != TRX_NOT_STARTED");
|
" trx->conc_state != TRX_NOT_STARTED");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all
|
if (all
|
||||||
|| (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
|
|| (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
|
||||||
|
|
||||||
|
/* Run the fast part of commit if we did not already. */
|
||||||
|
if ((trx->active_trans & TRX_ACTIVE_COMMIT_ORDERED) == 0) {
|
||||||
|
innobase_commit_ordered_2(trx, thd);
|
||||||
|
}
|
||||||
|
|
||||||
/* We were instructed to commit the whole transaction, or
|
/* We were instructed to commit the whole transaction, or
|
||||||
this is an SQL statement end and autocommit is on */
|
this is an SQL statement end and autocommit is on */
|
||||||
|
|
||||||
/* We need current binlog position for ibbackup to work.
|
/* We did the first part already in innobase_commit_ordered(),
|
||||||
Note, the position is current because of
|
Now finish by doing a write + flush of logs. */
|
||||||
prepare_commit_mutex */
|
|
||||||
retry:
|
|
||||||
if (innobase_commit_concurrency > 0) {
|
|
||||||
pthread_mutex_lock(&commit_cond_m);
|
|
||||||
commit_threads++;
|
|
||||||
|
|
||||||
if (commit_threads > innobase_commit_concurrency) {
|
|
||||||
commit_threads--;
|
|
||||||
pthread_cond_wait(&commit_cond,
|
|
||||||
&commit_cond_m);
|
|
||||||
pthread_mutex_unlock(&commit_cond_m);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pthread_mutex_unlock(&commit_cond_m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The following calls to read the MySQL binary log
|
|
||||||
file name and the position return consistent results:
|
|
||||||
1) Other InnoDB transactions cannot intervene between
|
|
||||||
these calls as we are holding prepare_commit_mutex.
|
|
||||||
2) Binary logging of other engines is not relevant
|
|
||||||
to InnoDB as all InnoDB requires is that committing
|
|
||||||
InnoDB transactions appear in the same order in the
|
|
||||||
MySQL binary log as they appear in InnoDB logs.
|
|
||||||
3) A MySQL log file rotation cannot happen because
|
|
||||||
MySQL protects against this by having a counter of
|
|
||||||
transactions in prepared state and it only allows
|
|
||||||
a rotation when the counter drops to zero. See
|
|
||||||
LOCK_prep_xids and COND_prep_xids in log.cc. */
|
|
||||||
trx->mysql_log_file_name = mysql_bin_log_file_name();
|
|
||||||
trx->mysql_log_offset = (ib_int64_t) mysql_bin_log_file_pos();
|
|
||||||
|
|
||||||
/* Don't do write + flush right now. For group commit
|
|
||||||
to work we want to do the flush after releasing the
|
|
||||||
prepare_commit_mutex. */
|
|
||||||
trx->flush_log_later = TRUE;
|
|
||||||
innobase_commit_low(trx);
|
|
||||||
trx->flush_log_later = FALSE;
|
|
||||||
|
|
||||||
if (innobase_commit_concurrency > 0) {
|
|
||||||
pthread_mutex_lock(&commit_cond_m);
|
|
||||||
commit_threads--;
|
|
||||||
pthread_cond_signal(&commit_cond);
|
|
||||||
pthread_mutex_unlock(&commit_cond_m);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trx->active_trans == 2) {
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&prepare_commit_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now do a write + flush of logs. */
|
|
||||||
trx_commit_complete_for_mysql(trx);
|
trx_commit_complete_for_mysql(trx);
|
||||||
trx->active_trans = 0;
|
trx->active_trans = 0;
|
||||||
|
|
||||||
@@ -3061,7 +3114,7 @@ innobase_savepoint(
|
|||||||
innobase_release_stat_resources(trx);
|
innobase_release_stat_resources(trx);
|
||||||
|
|
||||||
/* cannot happen outside of transaction */
|
/* cannot happen outside of transaction */
|
||||||
DBUG_ASSERT(trx->active_trans);
|
DBUG_ASSERT(trx->active_trans & TRX_ACTIVE_IN_MYSQL);
|
||||||
|
|
||||||
/* TODO: use provided savepoint data area to store savepoint data */
|
/* TODO: use provided savepoint data area to store savepoint data */
|
||||||
char name[64];
|
char name[64];
|
||||||
@@ -3091,7 +3144,7 @@ innobase_close_connection(
|
|||||||
|
|
||||||
ut_a(trx);
|
ut_a(trx);
|
||||||
|
|
||||||
if (trx->active_trans == 0
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0
|
||||||
&& trx->conc_state != TRX_NOT_STARTED) {
|
&& trx->conc_state != TRX_NOT_STARTED) {
|
||||||
|
|
||||||
sql_print_error("trx->active_trans == 0, but"
|
sql_print_error("trx->active_trans == 0, but"
|
||||||
@@ -5019,7 +5072,7 @@ no_commit:
|
|||||||
/* Altering to InnoDB format */
|
/* Altering to InnoDB format */
|
||||||
innobase_commit(ht, user_thd, 1);
|
innobase_commit(ht, user_thd, 1);
|
||||||
/* Note that this transaction is still active. */
|
/* Note that this transaction is still active. */
|
||||||
prebuilt->trx->active_trans = 1;
|
prebuilt->trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
/* We will need an IX lock on the destination table. */
|
/* We will need an IX lock on the destination table. */
|
||||||
prebuilt->sql_stat_start = TRUE;
|
prebuilt->sql_stat_start = TRUE;
|
||||||
} else {
|
} else {
|
||||||
@@ -5035,7 +5088,7 @@ no_commit:
|
|||||||
locks, so they have to be acquired again. */
|
locks, so they have to be acquired again. */
|
||||||
innobase_commit(ht, user_thd, 1);
|
innobase_commit(ht, user_thd, 1);
|
||||||
/* Note that this transaction is still active. */
|
/* Note that this transaction is still active. */
|
||||||
prebuilt->trx->active_trans = 1;
|
prebuilt->trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
/* Re-acquire the table lock on the source table. */
|
/* Re-acquire the table lock on the source table. */
|
||||||
row_lock_table_for_mysql(prebuilt, src_table, mode);
|
row_lock_table_for_mysql(prebuilt, src_table, mode);
|
||||||
/* We will need an IX lock on the destination table. */
|
/* We will need an IX lock on the destination table. */
|
||||||
@@ -8944,10 +8997,10 @@ ha_innobase::start_stmt(
|
|||||||
trx->detailed_error[0] = '\0';
|
trx->detailed_error[0] = '\0';
|
||||||
|
|
||||||
/* Set the MySQL flag to mark that there is an active transaction */
|
/* Set the MySQL flag to mark that there is an active transaction */
|
||||||
if (trx->active_trans == 0) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0) {
|
||||||
|
|
||||||
innobase_register_trx_and_stmt(ht, thd);
|
innobase_register_trx_and_stmt(ht, thd);
|
||||||
trx->active_trans = 1;
|
trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
} else {
|
} else {
|
||||||
innobase_register_stmt(ht, thd);
|
innobase_register_stmt(ht, thd);
|
||||||
}
|
}
|
||||||
@@ -9045,10 +9098,10 @@ ha_innobase::external_lock(
|
|||||||
|
|
||||||
/* Set the MySQL flag to mark that there is an active
|
/* Set the MySQL flag to mark that there is an active
|
||||||
transaction */
|
transaction */
|
||||||
if (trx->active_trans == 0) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0) {
|
||||||
|
|
||||||
innobase_register_trx_and_stmt(ht, thd);
|
innobase_register_trx_and_stmt(ht, thd);
|
||||||
trx->active_trans = 1;
|
trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
} else if (trx->n_mysql_tables_in_use == 0) {
|
} else if (trx->n_mysql_tables_in_use == 0) {
|
||||||
innobase_register_stmt(ht, thd);
|
innobase_register_stmt(ht, thd);
|
||||||
}
|
}
|
||||||
@@ -9146,7 +9199,7 @@ ha_innobase::external_lock(
|
|||||||
prebuilt->used_in_HANDLER = FALSE;
|
prebuilt->used_in_HANDLER = FALSE;
|
||||||
|
|
||||||
if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
|
if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
|
||||||
if (trx->active_trans != 0) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) != 0) {
|
||||||
innobase_commit(ht, thd, TRUE);
|
innobase_commit(ht, thd, TRUE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -9231,10 +9284,10 @@ ha_innobase::transactional_table_lock(
|
|||||||
/* MySQL is setting a new transactional table lock */
|
/* MySQL is setting a new transactional table lock */
|
||||||
|
|
||||||
/* Set the MySQL flag to mark that there is an active transaction */
|
/* Set the MySQL flag to mark that there is an active transaction */
|
||||||
if (trx->active_trans == 0) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0) {
|
||||||
|
|
||||||
innobase_register_trx_and_stmt(ht, thd);
|
innobase_register_trx_and_stmt(ht, thd);
|
||||||
trx->active_trans = 1;
|
trx->active_trans |= TRX_ACTIVE_IN_MYSQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (THDVAR(thd, table_locks) && thd_in_lock_tables(thd)) {
|
if (THDVAR(thd, table_locks) && thd_in_lock_tables(thd)) {
|
||||||
@@ -10288,7 +10341,8 @@ innobase_xa_prepare(
|
|||||||
|
|
||||||
innobase_release_stat_resources(trx);
|
innobase_release_stat_resources(trx);
|
||||||
|
|
||||||
if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {
|
if ((trx->active_trans & TRX_ACTIVE_IN_MYSQL) == 0 &&
|
||||||
|
trx->conc_state != TRX_NOT_STARTED) {
|
||||||
|
|
||||||
sql_print_error("trx->active_trans == 0, but trx->conc_state != "
|
sql_print_error("trx->active_trans == 0, but trx->conc_state != "
|
||||||
"TRX_NOT_STARTED");
|
"TRX_NOT_STARTED");
|
||||||
@@ -10300,7 +10354,7 @@ innobase_xa_prepare(
|
|||||||
/* We were instructed to prepare the whole transaction, or
|
/* We were instructed to prepare the whole transaction, or
|
||||||
this is an SQL statement end and autocommit is on */
|
this is an SQL statement end and autocommit is on */
|
||||||
|
|
||||||
ut_ad(trx->active_trans);
|
ut_ad(trx->active_trans & TRX_ACTIVE_IN_MYSQL);
|
||||||
|
|
||||||
error = (int) trx_prepare_for_mysql(trx);
|
error = (int) trx_prepare_for_mysql(trx);
|
||||||
} else {
|
} else {
|
||||||
@@ -10324,36 +10378,6 @@ innobase_xa_prepare(
|
|||||||
|
|
||||||
srv_active_wake_master_thread();
|
srv_active_wake_master_thread();
|
||||||
|
|
||||||
if (thd_sql_command(thd) != SQLCOM_XA_PREPARE &&
|
|
||||||
(all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
|
|
||||||
{
|
|
||||||
if (srv_enable_unsafe_group_commit && !THDVAR(thd, support_xa)) {
|
|
||||||
/* choose group commit rather than binlog order */
|
|
||||||
return(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For ibbackup to work the order of transactions in binlog
|
|
||||||
and InnoDB must be the same. Consider the situation
|
|
||||||
|
|
||||||
thread1> prepare; write to binlog; ...
|
|
||||||
<context switch>
|
|
||||||
thread2> prepare; write to binlog; commit
|
|
||||||
thread1> ... commit
|
|
||||||
|
|
||||||
To ensure this will not happen we're taking the mutex on
|
|
||||||
prepare, and releasing it on commit.
|
|
||||||
|
|
||||||
Note: only do it for normal commits, done via ha_commit_trans.
|
|
||||||
If 2pc protocol is executed by external transaction
|
|
||||||
coordinator, it will be just a regular MySQL client
|
|
||||||
executing XA PREPARE and XA COMMIT commands.
|
|
||||||
In this case we cannot know how many minutes or hours
|
|
||||||
will be between XA PREPARE and XA COMMIT, and we don't want
|
|
||||||
to block for undefined period of time. */
|
|
||||||
pthread_mutex_lock(&prepare_commit_mutex);
|
|
||||||
trx->active_trans = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(error);
|
return(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11638,11 +11662,6 @@ static MYSQL_SYSVAR_ENUM(adaptive_checkpoint, srv_adaptive_checkpoint,
|
|||||||
"Enable/Disable flushing along modified age. (none, reflex, [estimate])",
|
"Enable/Disable flushing along modified age. (none, reflex, [estimate])",
|
||||||
NULL, innodb_adaptive_checkpoint_update, 2, &adaptive_checkpoint_typelib);
|
NULL, innodb_adaptive_checkpoint_update, 2, &adaptive_checkpoint_typelib);
|
||||||
|
|
||||||
static MYSQL_SYSVAR_ULONG(enable_unsafe_group_commit, srv_enable_unsafe_group_commit,
|
|
||||||
PLUGIN_VAR_RQCMDARG,
|
|
||||||
"Enable/Disable unsafe group commit when support_xa=OFF and use with binlog or other XA storage engine.",
|
|
||||||
NULL, NULL, 0, 0, 1, 0);
|
|
||||||
|
|
||||||
static MYSQL_SYSVAR_ULONG(expand_import, srv_expand_import,
|
static MYSQL_SYSVAR_ULONG(expand_import, srv_expand_import,
|
||||||
PLUGIN_VAR_RQCMDARG,
|
PLUGIN_VAR_RQCMDARG,
|
||||||
"Enable/Disable converting automatically *.ibd files when import tablespace.",
|
"Enable/Disable converting automatically *.ibd files when import tablespace.",
|
||||||
@@ -11746,7 +11765,6 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
|
|||||||
MYSQL_SYSVAR(read_ahead),
|
MYSQL_SYSVAR(read_ahead),
|
||||||
MYSQL_SYSVAR(adaptive_checkpoint),
|
MYSQL_SYSVAR(adaptive_checkpoint),
|
||||||
MYSQL_SYSVAR(flush_log_at_trx_commit_session),
|
MYSQL_SYSVAR(flush_log_at_trx_commit_session),
|
||||||
MYSQL_SYSVAR(enable_unsafe_group_commit),
|
|
||||||
MYSQL_SYSVAR(expand_import),
|
MYSQL_SYSVAR(expand_import),
|
||||||
MYSQL_SYSVAR(extra_rsegments),
|
MYSQL_SYSVAR(extra_rsegments),
|
||||||
MYSQL_SYSVAR(dict_size_limit),
|
MYSQL_SYSVAR(dict_size_limit),
|
||||||
|
@@ -241,16 +241,6 @@ LEX_STRING *thd_query_string(MYSQL_THD thd);
|
|||||||
char **thd_query(MYSQL_THD thd);
|
char **thd_query(MYSQL_THD thd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Get the file name of the MySQL binlog.
|
|
||||||
* @return the name of the binlog file
|
|
||||||
*/
|
|
||||||
const char* mysql_bin_log_file_name(void);
|
|
||||||
|
|
||||||
/** Get the current position of the MySQL binlog.
|
|
||||||
* @return byte offset from the beginning of the binlog
|
|
||||||
*/
|
|
||||||
ulonglong mysql_bin_log_file_pos(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Check if a user thread is a replication slave thread
|
Check if a user thread is a replication slave thread
|
||||||
@param thd user thread
|
@param thd user thread
|
||||||
@@ -291,6 +281,11 @@ bool thd_binlog_filter_ok(const MYSQL_THD thd);
|
|||||||
#endif /* MYSQL_VERSION_ID > 50140 */
|
#endif /* MYSQL_VERSION_ID > 50140 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the file name and position of the MySQL binlog corresponding to the
|
||||||
|
* current commit.
|
||||||
|
*/
|
||||||
|
extern void mysql_bin_log_commit_pos(THD *thd, ulonglong *out_pos, const char **out_file);
|
||||||
|
|
||||||
typedef struct trx_struct trx_t;
|
typedef struct trx_struct trx_t;
|
||||||
/********************************************************************//**
|
/********************************************************************//**
|
||||||
@file handler/ha_innodb.h
|
@file handler/ha_innodb.h
|
||||||
|
@@ -511,9 +511,10 @@ struct trx_struct{
|
|||||||
in that case we must flush the log
|
in that case we must flush the log
|
||||||
in trx_commit_complete_for_mysql() */
|
in trx_commit_complete_for_mysql() */
|
||||||
ulint duplicates; /*!< TRX_DUP_IGNORE | TRX_DUP_REPLACE */
|
ulint duplicates; /*!< TRX_DUP_IGNORE | TRX_DUP_REPLACE */
|
||||||
ulint active_trans; /*!< 1 - if a transaction in MySQL
|
ulint active_trans; /*!< TRX_ACTIVE_IN_MYSQL - set if a
|
||||||
is active. 2 - if prepare_commit_mutex
|
transaction in MySQL is active.
|
||||||
was taken */
|
TRX_ACTIVE_COMMIT_ORDERED - set if
|
||||||
|
innobase_commit_ordered has run */
|
||||||
ulint has_search_latch;
|
ulint has_search_latch;
|
||||||
/* TRUE if this trx has latched the
|
/* TRUE if this trx has latched the
|
||||||
search system latch in S-mode */
|
search system latch in S-mode */
|
||||||
@@ -824,6 +825,10 @@ Multiple flags can be combined with bitwise OR. */
|
|||||||
#define TRX_SIG_OTHER_SESS 1 /* sent by another session (which
|
#define TRX_SIG_OTHER_SESS 1 /* sent by another session (which
|
||||||
must hold rights to this) */
|
must hold rights to this) */
|
||||||
|
|
||||||
|
/* Flag bits for trx_struct.active_trans */
|
||||||
|
#define TRX_ACTIVE_IN_MYSQL (1<<0)
|
||||||
|
#define TRX_ACTIVE_COMMIT_ORDERED (1<<1)
|
||||||
|
|
||||||
/** Commit node states */
|
/** Commit node states */
|
||||||
enum commit_node_state {
|
enum commit_node_state {
|
||||||
COMMIT_NODE_SEND = 1, /*!< about to send a commit signal to
|
COMMIT_NODE_SEND = 1, /*!< about to send a commit signal to
|
||||||
|
Reference in New Issue
Block a user