# # MDEV-36360: Don't grab table-level X locks for applied inserts. # # It prevents a debug crash in wsrep_report_error() which happened when appliers would run # with FK and UK checks disabled and erroneously execute plain inserts as bulk inserts. # # Moreover, in release builds such a behavior could lead to deadlocks between two applier # threads if a thread waiting for a table-level lock was ordered before the lock holder. # In that case the lock holder would proceed to commit order and wait forever for the # now-blocked other applier thread to commit before. # --source include/galera_cluster.inc --source include/have_innodb.inc --source include/have_debug_sync.inc --source include/have_debug.inc --let $galera_connection_name = node_3 --let $galera_server_number = 3 --source include/galera_connect.inc # Save original auto_increment_offset values. --let $node_1=node_1 --let $node_2=node_2 --let $node_3=node_3 --source ../galera/include/auto_increment_offset_save.inc # Create parent and child tables. --connection node_1 CREATE TABLE parent ( id INT PRIMARY KEY ) ENGINE=InnoDB; CREATE TABLE child ( id INT PRIMARY KEY, parent_id INT, KEY (parent_id), CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent(id) ) ENGINE=InnoDB; # Fill the parent table with rows that will later be used by the child. INSERT INTO parent VALUES (1), (2); # Wait until the rows are replicated on node #3. --connection node_3 --let $wait_condition = SELECT COUNT(*) = 2 FROM parent --source include/wait_condition.inc # Delete one row from the parent table on node #3 and rejoin the cluster. SET SESSION wsrep_on = OFF; DELETE FROM parent WHERE id = 1; SET SESSION wsrep_on = ON; --echo Restarting server 3 with one applier thread having FK and UK checks disabled --source include/shutdown_mysqld.inc --let $start_mysqld_params = --wsrep_slave_FK_checks=0 --wsrep_slave_UK_checks=0 --source ../galera/include/start_mysqld.inc # Stop the applier after writing a row into the child table. SET GLOBAL DEBUG_DBUG = 'd,sync.wsrep_after_write_row'; # Insert a child row that will be applied on node #3, but should not # grab table-level X-lock. --connection node_1 INSERT INTO child VALUES (1, 1); --connection node_3 SET DEBUG_SYNC = 'now WAIT_FOR sync.wsrep_after_write_row_reached'; # Now that the applier has hit the global sync point wait, reset it # so that the upcoming insert avoids it. SET GLOBAL DEBUG_DBUG = ''; # Don't wait for applied insert to commit. SET wsrep_sync_wait = 0; SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL signal.wsrep_after_write_row'; # The insert should pass the sync point, as otherwise if the applied insert # grabs table-level X-lock, they'll both deadlock forever. INSERT INTO child VALUES (2, 2); SET DEBUG_SYNC = 'RESET'; --let $assert_select = foreign key constraint fails --let $assert_count = 0 --let $assert_text = no FK constraint failure --let $assert_only_after = CURRENT_TEST --let $assert_file = $MYSQLTEST_VARDIR/log/mysqld.3.err --source include/assert_grep.inc # Child row insert is applied even though there's no parent row. --echo Server 3 SELECT COUNT(*) AS EXPECT_1 FROM parent; SELECT COUNT(*) AS EXPECT_2 FROM child; # Check other nodes have both parent and child rows. --connection node_1 --echo Server 1 SET wsrep_sync_wait = 15; SELECT COUNT(*) AS EXPECT_2 FROM parent; SELECT COUNT(*) AS EXPECT_2 FROM child; --connection node_2 --echo Server 2 SET wsrep_sync_wait = 15; SELECT COUNT(*) AS EXPECT_2 FROM parent; SELECT COUNT(*) AS EXPECT_2 FROM child; DROP TABLE child; DROP TABLE parent; # Restore original auto_increment_offset values. --source ../galera/include/auto_increment_offset_restore.inc --source include/galera_end.inc