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

MDEV-24119 MDL BF-BF Conflict caused by TRUNCATE TABLE

This PR fixes same issue as MDEV-21577 for TRUNCATE TABLE.
MDEV-21577 fixed TOI replication for OPTIMIZE, REPAIR and ALTER TABLE
operating on FK child table. It was later found out that also TRUNCATE
has similar problem and needs a fix.

The actual fix is to do FK parent table lookup before TRUNCATE TOI
isolation and append found FK parent table names in certification key
list for the write set.

PR contains also new test scenario in galera_ddl_fk_conflict test where
FK child has two FK parent tables and there are two DML transactions operating
on both parent tables.

For development convenience, new TO isolation macro was added:
WSREP_TO_ISOLATION_BEGIN_IF and WSREP_TO_ISOLATION_BEGIN_ALTER macro was changed
to skip the goto statement.
Reviewed-by: default avatarJan Lindström <jan.lindstrom@mariadb.com>
parent 83f3d12f
......@@ -23,15 +23,17 @@
#define DBUG_ASSERT_IF_WSREP(A) DBUG_ASSERT(A)
#define WSREP_MYSQL_DB (char *)"mysql"
#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \
#define WSREP_TO_ISOLATION_BEGIN_IF(db_, table_, table_list_) \
if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_))
#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \
if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \
goto wsrep_error_label;
#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, fk_tables_) \
if (WSREP(thd) && wsrep_thd_is_local(thd) && \
wsrep_to_isolation_begin(thd, db_, table_, \
table_list_, alter_info_, fk_tables_)) \
goto wsrep_error_label;
table_list_, alter_info_, fk_tables_))
#define WSREP_TO_ISOLATION_END \
if ((WSREP(thd) && wsrep_thd_is_local_toi(thd)) || \
......@@ -48,7 +50,7 @@
#define WSREP_TO_ISOLATION_BEGIN_FK_TABLES(db_, table_, table_list_, fk_tables) \
if (WSREP(thd) && !thd->lex->no_write_to_binlog \
&& wsrep_to_isolation_begin(thd, db_, table_, table_list_, NULL, fk_tables)) goto wsrep_error_label;
&& wsrep_to_isolation_begin(thd, db_, table_, table_list_, NULL, fk_tables))
#define WSREP_DEBUG(...) \
if (wsrep_debug) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
......
......@@ -40,31 +40,40 @@
--connection node_1
SET SESSION wsrep_sync_wait=0;
CREATE TABLE p (pk INTEGER PRIMARY KEY, f2 CHAR(30));
INSERT INTO p VALUES (1, 'INITIAL VALUE');
INSERT INTO p VALUES (2, 'INITIAL VALUE');
CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30));
INSERT INTO p1 VALUES (1, 'INITIAL VALUE');
CREATE TABLE c (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p(pk));
INSERT INTO c VALUES (1,1), (2,2);
CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30));
INSERT INTO p2 VALUES (1, 'INITIAL VALUE');
INSERT INTO p2 VALUES (2, 'INITIAL VALUE');
CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk));
INSERT INTO c1 VALUES (1,1);
CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk));
INSERT INTO c2 VALUES (1,1,1), (2,1,2);
--connection node_1
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1;
UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1;
--connection node_2
SET SESSION wsrep_sync_wait=0;
--eval $table_admin_command TABLE c $table_admin_command_end
--eval $table_admin_command TABLE c1 $table_admin_command_end
--connection node_1
--error ER_LOCK_DEADLOCK
COMMIT;
SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE';
--connection node_2
SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE';
--echo ######################################################################
--echo #
......@@ -81,7 +90,7 @@ BEGIN;
--source include/galera_set_sync_point.inc
--connection node_2
--eval $table_admin_command TABLE c $table_admin_command_end
--eval $table_admin_command TABLE c1 $table_admin_command_end
--connection node_1a
--source include/galera_wait_sync_point.inc
......@@ -89,7 +98,62 @@ BEGIN;
--let $expected_cert_failures = `SELECT VARIABLE_VALUE+1 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'`
--connection node_1
UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1;
UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1;
--send COMMIT
--connection node_1a
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_signal_sync_point.inc
--let $wait_condition = SELECT VARIABLE_VALUE = $expected_cert_failures FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'
--source include/wait_condition.inc
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
SELECT 'I deadlocked';
SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE';
--connection node_2
SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE';
--echo ######################################################################
--echo #
--echo # Scenario #3: 2 DMLs working on two FK parent tables try to replicate,
--echo # but fails in certification for earlier DDL on child table
--echo # which is child to both FK parents
--echo #
--echo ######################################################################
--connection node_1
BEGIN;
--connection node_1b
BEGIN;
# Block the applier on node #1 and issue DDL on node 2
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_set_sync_point.inc
--connection node_2
--eval $table_admin_command TABLE c2 $table_admin_command_end
--connection node_1a
--source include/galera_wait_sync_point.inc
--source include/galera_clear_sync_point.inc
--let $expected_cert_failures = `SELECT VARIABLE_VALUE+2 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'`
--connection node_1
UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1;
--send COMMIT
--connection node_1b
UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2;
--send COMMIT
--connection node_1a
......@@ -102,13 +166,19 @@ UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1;
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
SELECT 'I deadlocked';
--connection node_1b
--error ER_LOCK_DEADLOCK
--reap
SELECT 'I deadlocked';
SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE';
--connection node_2
SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE';
SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE';
DROP TABLE c;
DROP TABLE p;
DROP TABLE c1, c2;
DROP TABLE p1, p2;
......@@ -7,10 +7,16 @@
--source include/have_debug_sync.inc
--source include/galera_have_debug_sync.inc
# sync point controlling session
--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
--connection node_1a
SET SESSION wsrep_sync_wait=0;
# secondary conflicting DML victim session
--connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1
--connection node_1b
SET SESSION wsrep_sync_wait=0;
--let $table_admin_command = OPTIMIZE
--source galera_ddl_fk_conflict.inc
......@@ -21,5 +27,9 @@ SET SESSION wsrep_sync_wait=0;
--let $table_admin_command_end = ENGINE=INNODB
--source galera_ddl_fk_conflict.inc
--let $table_admin_command = TRUNCATE
--let $table_admin_command_end =
--source galera_ddl_fk_conflict.inc
# CHECK and ANALYZE are not affected
......@@ -453,7 +453,9 @@ static bool wsrep_toi_replication(THD *thd, TABLE_LIST *tables)
{
WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, tables);
} else {
WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, tables, &keys);
WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, tables, &keys) {
return true;
}
}
return false;
......
......@@ -480,7 +480,11 @@ bool Sql_cmd_alter_table::execute(THD *thd)
WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL),
(lex->name.str ? lex->name.str : NULL),
first_table, &alter_info, &keys);
first_table, &alter_info, &keys)
{
WSREP_WARN("ALTER TABLE isolation failure");
DBUG_RETURN(TRUE);
}
thd->variables.auto_increment_offset = 1;
thd->variables.auto_increment_increment = 1;
......@@ -522,11 +526,6 @@ bool Sql_cmd_alter_table::execute(THD *thd)
lex->ignore);
DBUG_RETURN(result);
#ifdef WITH_WSREP
wsrep_error_label:
WSREP_WARN("ALTER TABLE isolation failure");
DBUG_RETURN(TRUE);
#endif
}
bool Sql_cmd_discard_import_tablespace::execute(THD *thd)
......
......@@ -416,9 +416,23 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
bool hton_can_recreate;
#ifdef WITH_WSREP
if (WSREP(thd) &&
wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, 0))
DBUG_RETURN(TRUE);
if (WSREP(thd))
{
wsrep::key_array keys;
wsrep_append_fk_parent_table(thd, table_ref, &keys);
if (keys.empty())
{
WSREP_TO_ISOLATION_BEGIN_IF(table_ref->db.str, table_ref->table_name.str, NULL)
{
DBUG_RETURN(TRUE);
}
} else {
WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, table_ref, &keys)
{
DBUG_RETURN(TRUE);
}
}
}
#endif /* WITH_WSREP */
if (lock_table(thd, table_ref, &hton_can_recreate))
DBUG_RETURN(TRUE);
......
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