Commit f4f2e8fa authored by sjaakola's avatar sjaakola Committed by Jan Lindström

MW-402 cascading FK issues

* created tests focusing in multi-master conflicts during cascading foreign key
  processing
* in row0upd.cc, calling wsrep_row_ups_check_foreign_constraints only when
  running in cluster
* in row0ins.cc fixed regression from MW-369, which caused crash with MW-402.test
parent e5e33db5
...@@ -8,19 +8,20 @@ END; ...@@ -8,19 +8,20 @@ END;
INSERT INTO t1 VALUES (1, 'node 1'),(2, 'node 1'); INSERT INTO t1 VALUES (1, 'node 1'),(2, 'node 1');
INSERT INTO t1 VALUES (3, 'node 1'); INSERT INTO t1 VALUES (3, 'node 1');
END| END|
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb"; SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb";
INSERT INTO t1 VALUES (1, 'node 2');; INSERT INTO t1 VALUES (1, 'node 2');;
SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached";
SET SESSION wsrep_sync_wait = 0; SET SESSION wsrep_sync_wait = 0;
SET SESSION DEBUG_SYNC = 'wsrep_before_replication SIGNAL wsrep_before_replication_reached WAIT_FOR wsrep_before_replication_continue'; SET SESSION DEBUG_SYNC = 'wsrep_after_replication SIGNAL wsrep_after_replication_reached WAIT_FOR wsrep_after_replication_continue';
CALL insert_proc ();; CALL insert_proc ();;
SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_before_replication_reached"; SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_after_replication_reached";
SET GLOBAL DEBUG = ""; SET GLOBAL DEBUG = "";
SET DEBUG_SYNC = "now SIGNAL wsrep_before_replication_continue"; SET DEBUG_SYNC = "now SIGNAL wsrep_after_replication_continue";
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
SELECT @errno; SELECT @errno = 1213;
@errno @errno = 1213
1213 1
SELECT * FROM t1; SELECT * FROM t1;
f1 f2 f1 f2
1 node 2 1 node 2
...@@ -29,5 +30,6 @@ SELECT * FROM t1; ...@@ -29,5 +30,6 @@ SELECT * FROM t1;
f1 f2 f1 f2
1 node 2 1 node 2
3 node 1 3 node 1
SET GLOBAL wsrep_slave_threads = DEFAULT;
DROP TABLE t1; DROP TABLE t1;
DROP PROCEDURE insert_proc; DROP PROCEDURE insert_proc;
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p_id) REFERENCES p (f1) ON DELETE CASCADE);
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1, 0);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE c SET f2=1 where f1=1;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
DELETE FROM p WHERE f1 = 1;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p;
f1 f2
2 0
SELECT * FROM c;
f1 p_id f2
DROP TABLE c;
DROP TABLE p;
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p_id) REFERENCES p (f1) ON UPDATE CASCADE);
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1, 0);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE c SET f2=2 where f1=1;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
UPDATE p set f1=11 WHERE f1 = 1;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p;
f1 f2
2 0
11 0
SELECT * FROM c;
f1 p_id f2
1 11 0
DROP TABLE c;
DROP TABLE p;
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p_id) REFERENCES p (f1) ON UPDATE CASCADE);
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1, 0);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE c SET p_id=2 where f1=1;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
UPDATE p set f1=11 WHERE f1 = 1;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p;
f1 f2
2 0
11 0
SELECT * FROM c;
f1 p_id f2
1 11 0
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE p set f1=21 WHERE f1 = 11;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
UPDATE c SET p_id=2 where f1=1;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p;
f1 f2
2 0
11 0
SELECT * FROM c;
f1 p_id f2
1 2 0
DROP TABLE c;
DROP TABLE p;
CREATE TABLE p1 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE p2 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p1_id INTEGER, p2_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p1_id) REFERENCES p1 (f1) ON DELETE CASCADE,
CONSTRAINT fk_2 FOREIGN KEY (p2_id) REFERENCES p2 (f1));
INSERT INTO p1 VALUES (1, 0);
INSERT INTO p2 VALUES (1, 0);
INSERT INTO c VALUES (1, 1, 1, 0);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE p2 SET f2=2 where f1=1;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
DELETE FROM p1 WHERE f1 = 1;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
SELECT * FROM p1;
f1 f2
SELECT * FROM p2;
f1 f2
1 2
SELECT * FROM c;
f1 p1_id p2_id f2
DROP TABLE c;
DROP TABLE p1;
DROP TABLE p2;
...@@ -16,6 +16,18 @@ BEGIN ...@@ -16,6 +16,18 @@ BEGIN
END| END|
DELIMITER ;| DELIMITER ;|
# We need two slave threads here to guarantee progress.
# If we use only one thread the following could happen
# in node_1:
# We block the only slave thread in wsrep_apply_cb and we
# issue an INSERT (by calling the stored procedure) that will
# try to acquire galera's local monitor in pre_commit().
# This usually works fine, except for when a commit cut event
# sneaks in the slave queue and gets a local seqno smaller than
# that of the INSERT. Because there is only one slave thread,
# commit cut is not processed and therefore does not advance
# local monitor, and our INSERT remains stuck there.
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb"; SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb";
--connection node_2 --connection node_2
...@@ -27,15 +39,15 @@ SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; ...@@ -27,15 +39,15 @@ SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached";
--connection node_1 --connection node_1
SET SESSION wsrep_sync_wait = 0; SET SESSION wsrep_sync_wait = 0;
SET SESSION DEBUG_SYNC = 'wsrep_before_replication SIGNAL wsrep_before_replication_reached WAIT_FOR wsrep_before_replication_continue'; SET SESSION DEBUG_SYNC = 'wsrep_after_replication SIGNAL wsrep_after_replication_reached WAIT_FOR wsrep_after_replication_continue';
--send CALL insert_proc (); --send CALL insert_proc ();
--connection node_1a --connection node_1a
SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_before_replication_reached"; SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_after_replication_reached";
--connection node_1a
SET GLOBAL DEBUG = ""; SET GLOBAL DEBUG = "";
SET DEBUG_SYNC = "now SIGNAL wsrep_before_replication_continue"; SET DEBUG_SYNC = "now SIGNAL wsrep_after_replication_continue";
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
--connection node_2 --connection node_2
...@@ -44,11 +56,13 @@ SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; ...@@ -44,11 +56,13 @@ SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
--connection node_1 --connection node_1
# We expect no errors here, because the handler in insert_proc() caught the deadlock error # We expect no errors here, because the handler in insert_proc() caught the deadlock error
--reap --reap
SELECT @errno; SELECT @errno = 1213;
SELECT * FROM t1; SELECT * FROM t1;
--connection node_2 --connection node_2
SELECT * FROM t1; SELECT * FROM t1;
--connection node_1
SET GLOBAL wsrep_slave_threads = DEFAULT;
DROP TABLE t1; DROP TABLE t1;
DROP PROCEDURE insert_proc; DROP PROCEDURE insert_proc;
\ No newline at end of file
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_debug_sync.inc
--source suite/galera/include/galera_have_debug_sync.inc
#
# we must open connection node_1a here, MW-369.inc will use it later
#
--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
#
# cascading delete operation is replicated from node2
# and this conflicts with an update for child table in node1
#
# As a result, the update should fail for certification error
#
--connection node_1
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p_id) REFERENCES p (f1) ON DELETE CASCADE);
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1, 0);
--let $mw_369_parent_query = UPDATE c SET f2=1 where f1=1
--let $mw_369_child_query = DELETE FROM p WHERE f1 = 1
--connection node_1a
--source MW-369.inc
# Commit fails
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
--connection node_2
SELECT * FROM p;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p;
#
# cascading update operation is replicated from node2
# and this conflicts with an update for child table in node1
#
# As a result, the update should fail for certification error
#
--connection node_1
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p_id) REFERENCES p (f1) ON UPDATE CASCADE);
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1, 0);
--let $mw_369_parent_query = UPDATE c SET f2=2 where f1=1
--let $mw_369_child_query = UPDATE p set f1=11 WHERE f1 = 1
--connection node_1a
--source MW-369.inc
# Commit fails
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
--connection node_2
SELECT * FROM p;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p;
#
# ON UPDATE CASCADE tests
# Here we update primary key of parent table to cause cascaded update
# on child table
#
# cascading update operation is replicated from node2
# and this conflicts with an update for child table in node1
#
# As a result, the update should fail for certification error
#
--connection node_1
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p_id) REFERENCES p (f1) ON UPDATE CASCADE);
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1, 0);
--let $mw_369_parent_query = UPDATE c SET p_id=2 where f1=1
--let $mw_369_child_query = UPDATE p set f1=11 WHERE f1 = 1
--connection node_1a
--source MW-369.inc
# Commit fails
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
# same as previous, but statements in different order
--connection node_2
SELECT * FROM p;
SELECT * FROM c;
--let $mw_369_parent_query = UPDATE p set f1=21 WHERE f1 = 11
--let $mw_369_child_query = UPDATE c SET p_id=2 where f1=1
--connection node_1a
--source MW-369.inc
# Commit fails
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
--connection node_2
SELECT * FROM p;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p;
#
# CASCADE DELETE tests with two parent tables
# Here we cause cascaded operation on child table through
# one parent table and have other operation on the other
# parent table
#
# cascading update operation is replicated from node2
# but this does not conflict with an update for the other parent table in node1
#
# As a result, the update on p2 should succeed
#
--connection node_1
CREATE TABLE p1 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE p2 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p1_id INTEGER, p2_id INTEGER, f2 INTEGER,
CONSTRAINT fk_1 FOREIGN KEY (p1_id) REFERENCES p1 (f1) ON DELETE CASCADE,
CONSTRAINT fk_2 FOREIGN KEY (p2_id) REFERENCES p2 (f1));
INSERT INTO p1 VALUES (1, 0);
INSERT INTO p2 VALUES (1, 0);
INSERT INTO c VALUES (1, 1, 1, 0);
--let $mw_369_parent_query = UPDATE p2 SET f2=2 where f1=1
--let $mw_369_child_query = DELETE FROM p1 WHERE f1 = 1
--connection node_1a
--source MW-369.inc
# Commit succeeds
--connection node_1
--reap
# same as previous, but statements in different order
--connection node_2
SELECT * FROM p1;
SELECT * FROM p2;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p1;
DROP TABLE p2;
\ No newline at end of file
...@@ -498,6 +498,9 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all) ...@@ -498,6 +498,9 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
} }
mysql_mutex_lock(&thd->LOCK_wsrep_thd); mysql_mutex_lock(&thd->LOCK_wsrep_thd);
DEBUG_SYNC(thd, "wsrep_after_replication");
switch(rcode) { switch(rcode) {
case 0: case 0:
/* /*
......
...@@ -1295,8 +1295,7 @@ row_ins_foreign_check_on_constraint( ...@@ -1295,8 +1295,7 @@ row_ins_foreign_check_on_constraint(
foreign, foreign,
clust_rec, clust_rec,
clust_index, clust_index,
FALSE, FALSE, FALSE);
(node) ? TRUE : FALSE);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
fprintf(stderr, fprintf(stderr,
"WSREP: foreign key append failed: %d\n", err); "WSREP: foreign key append failed: %d\n", err);
......
...@@ -1980,8 +1980,8 @@ row_upd_sec_index_entry( ...@@ -1980,8 +1980,8 @@ row_upd_sec_index_entry(
} }
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (wsrep_on(trx->mysql_thd) && if (wsrep_on(trx->mysql_thd) &&
!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
err == DB_SUCCESS && !referenced && err == DB_SUCCESS && !referenced &&
!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
!(parent && que_node_get_type(parent) == !(parent && que_node_get_type(parent) ==
QUE_NODE_UPDATE && QUE_NODE_UPDATE &&
((upd_node_t*)parent)->cascade_node == node) && ((upd_node_t*)parent)->cascade_node == node) &&
......
...@@ -1301,8 +1301,7 @@ row_ins_foreign_check_on_constraint( ...@@ -1301,8 +1301,7 @@ row_ins_foreign_check_on_constraint(
foreign, foreign,
clust_rec, clust_rec,
clust_index, clust_index,
FALSE, FALSE, FALSE);
(node) ? TRUE : FALSE);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
fprintf(stderr, fprintf(stderr,
"WSREP: foreign key append failed: %d\n", err); "WSREP: foreign key append failed: %d\n", err);
......
...@@ -1985,8 +1985,8 @@ row_upd_sec_index_entry( ...@@ -1985,8 +1985,8 @@ row_upd_sec_index_entry(
} }
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (wsrep_on(trx->mysql_thd) && if (wsrep_on(trx->mysql_thd) &&
!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
err == DB_SUCCESS && !referenced && err == DB_SUCCESS && !referenced &&
!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
!(parent && que_node_get_type(parent) == !(parent && que_node_get_type(parent) ==
QUE_NODE_UPDATE && QUE_NODE_UPDATE &&
((upd_node_t*)parent)->cascade_node == node) && ((upd_node_t*)parent)->cascade_node == node) &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment