mirror of
https://github.com/MariaDB/server.git
synced 2026-05-14 19:07:15 +02:00
In parallel replication, there are two kinds of events which are executed in different ways. Normal events that are part of event groups/transactions are executed asynchroneously by being queued for a worker thread. Other events like format description and rotate and such are executed directly in the driver SQL thread. If the direct execution of the other events were to update the old-style position, then the position gets updated too far ahead, before the normal events that have been queued for a worker thread have been executed. So this patch adds some special cases to prevent such position updates ahead of time, and instead queues dummy events for the worker threads, so that they will at an appropriate time do the position updates instead. (Also fix a race in a test case that happened to trigger while running tests for this patch).
353 lines
11 KiB
Text
353 lines
11 KiB
Text
--source include/have_innodb.inc
|
|
--source include/have_debug.inc
|
|
--source include/have_debug_sync.inc
|
|
--let $rpl_topology=1->2
|
|
--source include/rpl_init.inc
|
|
|
|
# Test various aspects of parallel replication.
|
|
|
|
--connection server_2
|
|
SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads;
|
|
--error ER_SLAVE_MUST_STOP
|
|
SET GLOBAL slave_parallel_threads=10;
|
|
--source include/stop_slave.inc
|
|
SET GLOBAL slave_parallel_threads=10;
|
|
CHANGE MASTER TO master_use_gtid=slave_pos;
|
|
--source include/start_slave.inc
|
|
|
|
|
|
--echo *** Test long-running query in domain 1 can run in parallel with short queries in domain 0 ***
|
|
|
|
--connection server_1
|
|
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
|
|
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
|
|
INSERT INTO t1 VALUES (1);
|
|
INSERT INTO t2 VALUES (1);
|
|
--save_master_pos
|
|
|
|
--connection server_2
|
|
--sync_with_master
|
|
|
|
# Block the table t1 to simulate a replicated query taking a long time.
|
|
--connect (con_temp1,127.0.0.1,root,,test,$SERVER_MYPORT_2,)
|
|
LOCK TABLE t1 WRITE;
|
|
|
|
--connection server_1
|
|
SET gtid_domain_id=1;
|
|
# This query will be blocked on the slave until UNLOCK TABLES.
|
|
INSERT INTO t1 VALUES (2);
|
|
SET gtid_domain_id=0;
|
|
# These t2 queries can be replicated in parallel with the prior t1 query, as
|
|
# they are in a separate replication domain.
|
|
INSERT INTO t2 VALUES (2);
|
|
INSERT INTO t2 VALUES (3);
|
|
BEGIN;
|
|
INSERT INTO t2 VALUES (4);
|
|
INSERT INTO t2 VALUES (5);
|
|
COMMIT;
|
|
INSERT INTO t2 VALUES (6);
|
|
|
|
--connection server_2
|
|
--let $wait_condition= SELECT COUNT(*) = 6 FROM t2
|
|
--source include/wait_condition.inc
|
|
|
|
SELECT * FROM t2 ORDER by a;
|
|
|
|
--connection con_temp1
|
|
SELECT * FROM t1;
|
|
UNLOCK TABLES;
|
|
|
|
--connection server_2
|
|
--let $wait_condition= SELECT COUNT(*) = 2 FROM t1
|
|
--source include/wait_condition.inc
|
|
|
|
SELECT * FROM t1 ORDER BY a;
|
|
|
|
|
|
--echo *** Test two transactions in different domains committed in opposite order on slave but in a single group commit. ***
|
|
--connection server_2
|
|
--source include/stop_slave.inc
|
|
|
|
--connection server_1
|
|
# Use a stored function to inject a debug_sync into the appropriate THD.
|
|
# The function does nothing on the master, and on the slave it injects the
|
|
# desired debug_sync action(s).
|
|
SET sql_log_bin=0;
|
|
--delimiter ||
|
|
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
|
|
RETURNS INT DETERMINISTIC
|
|
BEGIN
|
|
RETURN x;
|
|
END
|
|
||
|
|
--delimiter ;
|
|
SET sql_log_bin=1;
|
|
|
|
SET @old_format= @@SESSION.binlog_format;
|
|
SET binlog_format='statement';
|
|
SET gtid_domain_id=1;
|
|
INSERT INTO t2 VALUES (foo(10,
|
|
'commit_before_enqueue SIGNAL ready1 WAIT_FOR cont1',
|
|
'commit_after_release_LOCK_prepare_ordered SIGNAL ready2'));
|
|
|
|
--connection server_2
|
|
FLUSH LOGS;
|
|
--source include/wait_for_binlog_checkpoint.inc
|
|
SET sql_log_bin=0;
|
|
--delimiter ||
|
|
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
|
|
RETURNS INT DETERMINISTIC
|
|
BEGIN
|
|
IF d1 != '' THEN
|
|
SET debug_sync = d1;
|
|
END IF;
|
|
IF d2 != '' THEN
|
|
SET debug_sync = d2;
|
|
END IF;
|
|
RETURN x;
|
|
END
|
|
||
|
|
--delimiter ;
|
|
SET sql_log_bin=1;
|
|
SET @old_format=@@GLOBAL.binlog_format;
|
|
SET GLOBAL binlog_format=statement;
|
|
# We need to restart all parallel threads for the new global setting to
|
|
# be copied to the session-level values.
|
|
SET GLOBAL slave_parallel_threads=0;
|
|
SET GLOBAL slave_parallel_threads=10;
|
|
--source include/start_slave.inc
|
|
|
|
# First make sure the first insert is ready to commit, but not queued yet.
|
|
SET debug_sync='now WAIT_FOR ready1';
|
|
|
|
--connection server_1
|
|
SET gtid_domain_id=2;
|
|
INSERT INTO t2 VALUES (foo(11,
|
|
'commit_before_enqueue SIGNAL ready3 WAIT_FOR cont3',
|
|
'commit_after_release_LOCK_prepare_ordered SIGNAL ready4 WAIT_FOR cont4'));
|
|
SET gtid_domain_id=0;
|
|
SELECT * FROM t2 WHERE a >= 10 ORDER BY a;
|
|
|
|
--connection server_2
|
|
# Now wait for the second insert to queue itself as the leader, and then
|
|
# wait for more commits to queue up.
|
|
SET debug_sync='now WAIT_FOR ready3';
|
|
SET debug_sync='now SIGNAL cont3';
|
|
SET debug_sync='now WAIT_FOR ready4';
|
|
# Now allow the first insert to queue up to participate in group commit.
|
|
SET debug_sync='now SIGNAL cont1';
|
|
SET debug_sync='now WAIT_FOR ready2';
|
|
# Finally allow the second insert to proceed and do the group commit.
|
|
SET debug_sync='now SIGNAL cont4';
|
|
|
|
--let $wait_condition= SELECT COUNT(*) = 2 FROM t2 WHERE a >= 10
|
|
--source include/wait_condition.inc
|
|
SELECT * FROM t2 WHERE a >= 10 ORDER BY a;
|
|
# The two INSERT transactions should have been committed in opposite order,
|
|
# but in the same group commit (seen by precense of cid=# in the SHOW
|
|
# BINLOG output).
|
|
--let $binlog_file= slave-bin.000002
|
|
--source include/show_binlog_events.inc
|
|
FLUSH LOGS;
|
|
--source include/wait_for_binlog_checkpoint.inc
|
|
|
|
# Restart all the slave parallel worker threads, to clear all debug_sync actions.
|
|
--connection server_2
|
|
--source include/stop_slave.inc
|
|
SET GLOBAL slave_parallel_threads=0;
|
|
SET GLOBAL slave_parallel_threads=10;
|
|
SET debug_sync='RESET';
|
|
--source include/start_slave.inc
|
|
|
|
|
|
--echo *** Test that group-committed transactions on the master can replicate in parallel on the slave. ***
|
|
--connection server_1
|
|
FLUSH LOGS;
|
|
--source include/wait_for_binlog_checkpoint.inc
|
|
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
|
|
# Create some sentinel rows so that the rows inserted in parallel fall into
|
|
# separate gaps and do not cause gap lock conflicts.
|
|
INSERT INTO t3 VALUES (1,1), (3,3), (5,5), (7,7);
|
|
--save_master_pos
|
|
--connection server_2
|
|
--sync_with_master
|
|
|
|
# We want to test that the transactions can execute out-of-order on
|
|
# the slave, but still end up committing in-order, and in a single
|
|
# group commit.
|
|
#
|
|
# The idea is to group-commit three transactions together on the master:
|
|
# A, B, and C. On the slave, C will execute the insert first, then A,
|
|
# and then B. But B manages to complete before A has time to commit, so
|
|
# all three end up committing together.
|
|
#
|
|
# So we start by setting up some row locks that will block transactions
|
|
# A and B from executing, allowing C to run first.
|
|
|
|
--connection con_temp1
|
|
BEGIN;
|
|
INSERT INTO t3 VALUES (2,102);
|
|
--connect (con_temp2,127.0.0.1,root,,test,$SERVER_MYPORT_2,)
|
|
BEGIN;
|
|
INSERT INTO t3 VALUES (4,104);
|
|
|
|
# On the master, queue three INSERT transactions as a single group commit.
|
|
--connect (con_temp3,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
|
|
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued1 WAIT_FOR master_cont1';
|
|
SET binlog_format=statement;
|
|
send INSERT INTO t3 VALUES (2, foo(12,
|
|
'commit_after_release_LOCK_prepare_ordered SIGNAL slave_queued1 WAIT_FOR slave_cont1',
|
|
''));
|
|
|
|
--connection server_1
|
|
SET debug_sync='now WAIT_FOR master_queued1';
|
|
|
|
--connect (con_temp4,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
|
|
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued2';
|
|
SET binlog_format=statement;
|
|
send INSERT INTO t3 VALUES (4, foo(14,
|
|
'commit_after_release_LOCK_prepare_ordered SIGNAL slave_queued2',
|
|
''));
|
|
|
|
--connection server_1
|
|
SET debug_sync='now WAIT_FOR master_queued2';
|
|
|
|
--connect (con_temp5,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
|
|
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued3';
|
|
SET binlog_format=statement;
|
|
send INSERT INTO t3 VALUES (6, foo(16,
|
|
'group_commit_waiting_for_prior SIGNAL slave_queued3',
|
|
''));
|
|
|
|
--connection server_1
|
|
SET debug_sync='now WAIT_FOR master_queued3';
|
|
SET debug_sync='now SIGNAL master_cont1';
|
|
|
|
--connection con_temp3
|
|
REAP;
|
|
--connection con_temp4
|
|
REAP;
|
|
--connection con_temp5
|
|
REAP;
|
|
|
|
--connection server_1
|
|
SELECT * FROM t3 ORDER BY a;
|
|
--let $binlog_file= master-bin.000002
|
|
--source include/show_binlog_events.inc
|
|
|
|
# First, wait until insert 3 is ready to queue up for group commit, but is
|
|
# waiting for insert 2 to commit before it can do so itself.
|
|
--connection server_2
|
|
SET debug_sync='now WAIT_FOR slave_queued3';
|
|
|
|
# Next, let insert 1 proceed, and allow it to queue up as the group commit
|
|
# leader, but let it wait for insert 2 to also queue up before proceeding.
|
|
--connection con_temp1
|
|
ROLLBACK;
|
|
--connection server_2
|
|
SET debug_sync='now WAIT_FOR slave_queued1';
|
|
|
|
# Now let insert 2 proceed and queue up.
|
|
--connection con_temp2
|
|
ROLLBACK;
|
|
--connection server_2
|
|
SET debug_sync='now WAIT_FOR slave_queued2';
|
|
# And finally, we can let insert 1 proceed and do the group commit with all
|
|
# three insert transactions together.
|
|
SET debug_sync='now SIGNAL slave_cont1';
|
|
|
|
# Wait for the commit to complete and check that all three transactions
|
|
# group-committed together (will be seen in the binlog as all three having
|
|
# cid=# on their GTID event).
|
|
--let $wait_condition= SELECT COUNT(*) = 3 FROM t3 WHERE a IN (2,4,6)
|
|
--source include/wait_condition.inc
|
|
SELECT * FROM t3 ORDER BY a;
|
|
--let $binlog_file= slave-bin.000003
|
|
--source include/show_binlog_events.inc
|
|
|
|
|
|
--echo *** Test STOP SLAVE in parallel mode ***
|
|
--connection server_2
|
|
--source include/stop_slave.inc
|
|
|
|
--connection server_1
|
|
# Set up a couple of transactions. The first will be blocked halfway
|
|
# through on a lock, and while it is blocked we initiate STOP SLAVE.
|
|
# We then test that the halfway-initiated transaction is allowed to
|
|
# complete, but no subsequent ones.
|
|
# We have to use statement-based mode and set
|
|
# binlog_direct_non_transactional_updates=0; otherwise the binlog will
|
|
# be split into two event groups, one for the MyISAM part and one for the
|
|
# InnoDB part.
|
|
SET binlog_direct_non_transactional_updates=0;
|
|
SET sql_log_bin=0;
|
|
CALL mtr.add_suppression("Statement is unsafe because it accesses a non-transactional table after accessing a transactional table within the same transaction");
|
|
SET sql_log_bin=1;
|
|
BEGIN;
|
|
INSERT INTO t2 VALUES (20);
|
|
--disable_warnings
|
|
INSERT INTO t1 VALUES (20);
|
|
--disable_warnings
|
|
INSERT INTO t2 VALUES (21);
|
|
INSERT INTO t3 VALUES (20, 20);
|
|
COMMIT;
|
|
INSERT INTO t3 VALUES(21, 21);
|
|
INSERT INTO t3 VALUES(22, 22);
|
|
SET binlog_format=@old_format;
|
|
--save_master_pos
|
|
|
|
# Start a connection that will block the replicated transaction halfway.
|
|
--connection con_temp1
|
|
BEGIN;
|
|
INSERT INTO t2 VALUES (21);
|
|
|
|
--connection server_2
|
|
START SLAVE;
|
|
# Wait for the MyISAM change to be visible, after which replication will wait
|
|
# for con_temp1 to roll back.
|
|
--let $wait_condition= SELECT COUNT(*) = 1 FROM t1 WHERE a=20
|
|
--source include/wait_condition.inc
|
|
|
|
--connection con_temp2
|
|
# Initiate slave stop. It will have to wait for the current event group
|
|
# to complete.
|
|
send STOP SLAVE;
|
|
|
|
--connection con_temp1
|
|
ROLLBACK;
|
|
|
|
--connection con_temp2
|
|
reap;
|
|
|
|
--connection server_2
|
|
--source include/wait_for_slave_to_stop.inc
|
|
# We should see the first transaction applied, but not the two others.
|
|
SELECT * FROM t1 WHERE a >= 20 ORDER BY a;
|
|
SELECT * FROM t2 WHERE a >= 20 ORDER BY a;
|
|
SELECT * FROM t3 WHERE a >= 20 ORDER BY a;
|
|
|
|
--source include/start_slave.inc
|
|
--sync_with_master
|
|
SELECT * FROM t1 WHERE a >= 20 ORDER BY a;
|
|
SELECT * FROM t2 WHERE a >= 20 ORDER BY a;
|
|
SELECT * FROM t3 WHERE a >= 20 ORDER BY a;
|
|
|
|
|
|
--connection server_2
|
|
--source include/stop_slave.inc
|
|
SET GLOBAL binlog_format=@old_format;
|
|
SET GLOBAL slave_parallel_threads=0;
|
|
SET GLOBAL slave_parallel_threads=10;
|
|
--source include/start_slave.inc
|
|
|
|
|
|
--connection server_2
|
|
--source include/stop_slave.inc
|
|
SET GLOBAL slave_parallel_threads=@old_parallel_threads;
|
|
--source include/start_slave.inc
|
|
|
|
--connection server_1
|
|
DROP function foo;
|
|
DROP TABLE t1,t2,t3;
|
|
|
|
--source include/rpl_end.inc
|