Commit 4a8291fc authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-30531 Corrupt index(es) on busy table when using FOREIGN KEY

lock_wait(): Never return the transient error code DB_LOCK_WAIT.
In commit 78a04a4c (MDEV-29869)
some assignments assign trx->error_state = DB_SUCCESS were removed,
and it was possible that the field was left at its initial value
DB_LOCK_WAIT.

The test case for this is nondeterministic; without this fix, it
would only occasionally fail.

Reviewed by: Vladislav Lesin
parent e039720b
......@@ -1029,7 +1029,39 @@ CREATE OR REPLACE TABLE t1 (b INT) ENGINE=InnoDB;
connection default;
INSERT INTO t2 VALUES (1);
connection con1;
disconnect con1;
connection default;
DROP TABLE IF EXISTS t2, t1;
#
# MDEV-30531 Corrupt index(es) on busy table when using FOREIGN KEY
#
SET @freq=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
CREATE TABLE collections (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
collectionhash varchar(255) NOT NULL DEFAULT '0',
PRIMARY KEY (id),
UNIQUE KEY ix_collection_collectionhash (collectionhash)
) ENGINE=InnoDB;
CREATE TABLE binaries (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
collections_id int(11) unsigned NOT NULL DEFAULT 0,
binaryhash binary(16) NOT NULL DEFAULT '0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
PRIMARY KEY (id),
UNIQUE KEY ix_binary_binaryhash (binaryhash),
CONSTRAINT FK_collections FOREIGN KEY (collections_id) REFERENCES collections (id) ON DELETE CASCADE
) ENGINE=InnoDB;
INSERT INTO collections (id) VALUES (NULL);
connection con1;
INSERT INTO binaries (id,collections_id) VALUES (NULL,1);
REPLACE INTO collections (id) VALUES (NULL);
connection default;
REPLACE INTO binaries (id) VALUES (NULL);
SET GLOBAL innodb_max_purge_lag_wait=0;
CHECK TABLE binaries, collections EXTENDED;
Table Op Msg_type Msg_text
test.binaries check status OK
test.collections check status OK
SET GLOBAL innodb_purge_rseg_truncate_frequency=@freq;
disconnect con1;
DROP TABLE binaries, collections;
# End of 10.6 tests
......@@ -1071,12 +1071,53 @@ INSERT INTO t2 VALUES (1);
--reap
# Cleanup
--disconnect con1
--connection default
--disable_warnings
DROP TABLE IF EXISTS t2, t1;
--enable_warnings
--echo #
--echo # MDEV-30531 Corrupt index(es) on busy table when using FOREIGN KEY
--echo #
SET @freq=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
CREATE TABLE collections (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
collectionhash varchar(255) NOT NULL DEFAULT '0',
PRIMARY KEY (id),
UNIQUE KEY ix_collection_collectionhash (collectionhash)
) ENGINE=InnoDB;
CREATE TABLE binaries (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
collections_id int(11) unsigned NOT NULL DEFAULT 0,
binaryhash binary(16) NOT NULL DEFAULT '0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
PRIMARY KEY (id),
UNIQUE KEY ix_binary_binaryhash (binaryhash),
CONSTRAINT FK_collections FOREIGN KEY (collections_id) REFERENCES collections (id) ON DELETE CASCADE
) ENGINE=InnoDB;
INSERT INTO collections (id) VALUES (NULL);
--connection con1
INSERT INTO binaries (id,collections_id) VALUES (NULL,1);
--send
REPLACE INTO collections (id) VALUES (NULL);
--connection default
--error 0,ER_LOCK_DEADLOCK,ER_NO_REFERENCED_ROW_2
REPLACE INTO binaries (id) VALUES (NULL);
SET GLOBAL innodb_max_purge_lag_wait=0;
CHECK TABLE binaries, collections EXTENDED;
SET GLOBAL innodb_purge_rseg_truncate_frequency=@freq;
--disconnect con1
# Cleanup
DROP TABLE binaries, collections;
--echo # End of 10.6 tests
--source include/wait_until_count_sessions.inc
......@@ -1903,7 +1903,6 @@ dberr_t lock_wait(que_thr_t *thr)
thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE)
? THD_WAIT_TABLE_LOCK : THD_WAIT_ROW_LOCK);
int err= 0;
mysql_mutex_lock(&lock_sys.wait_mutex);
/* Now that we are holding lock_sys.wait_mutex, we must reload
trx->lock.wait_mutex. It cannot be cleared as long as we are holding
......@@ -1956,30 +1955,51 @@ dberr_t lock_wait(que_thr_t *thr)
wait_lock= lock_wait_rpl_report(trx);
#endif
if (trx->error_state != DB_SUCCESS)
goto check_trx_error;
switch (trx->error_state) {
case DB_SUCCESS:
break;
case DB_LOCK_WAIT:
trx->error_state= DB_SUCCESS;
break;
default:
#ifdef UNIV_DEBUG
ut_ad("invalid state" == 0);
break;
case DB_DEADLOCK:
case DB_INTERRUPTED:
#endif
goto end_loop;
}
while (wait_lock)
{
int err;
ut_ad(trx->lock.wait_lock);
DEBUG_SYNC_C("lock_wait_before_suspend");
if (no_timeout)
{
my_cond_wait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex);
err= 0;
}
else
err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex,
&abstime);
wait_lock= trx->lock.wait_lock;
check_trx_error:
switch (trx->error_state) {
case DB_DEADLOCK:
case DB_INTERRUPTED:
break;
#ifdef UNIV_DEBUG
case DB_LOCK_WAIT_TIMEOUT:
case DB_LOCK_WAIT:
ut_ad("invalid state" == 0);
break;
#endif
default:
ut_ad(trx->error_state != DB_LOCK_WAIT_TIMEOUT);
/* Dictionary transactions must ignore KILL, because they could
be executed as part of a multi-transaction DDL operation,
such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */
......@@ -2001,6 +2021,7 @@ dberr_t lock_wait(que_thr_t *thr)
break;
}
end_loop:
if (row_lock_wait)
lock_sys.wait_resume(trx->mysql_thd, suspend_time, my_hrtime_coarse());
......@@ -2025,6 +2046,18 @@ dberr_t lock_wait(que_thr_t *thr)
});
thd_wait_end(trx->mysql_thd);
#ifdef UNIV_DEBUG
switch (trx->error_state) {
case DB_SUCCESS:
case DB_DEADLOCK:
case DB_INTERRUPTED:
case DB_LOCK_WAIT_TIMEOUT:
break;
default:
ut_ad("invalid state" == 0);
}
#endif
return trx->error_state;
}
......
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