[MDEV-25919] InnoDB reports misleading lock wait timeout on DDL operations Created: 2021-06-15  Updated: 2024-01-05  Resolved: 2021-08-31

Status: Closed
Project: MariaDB Server
Component/s: Locking, Storage Engine - InnoDB
Affects Version/s: 10.6.2
Fix Version/s: 10.6.5

Type: Bug Priority: Major
Reporter: Marko Mäkelä Assignee: Marko Mäkelä
Resolution: Fixed Votes: 1
Labels: regression

Issue Links:
Blocks
blocks MDEV-18381 Server crashes in ha_innobase::store_... Closed
blocks MDEV-25920 Atomic DROP DATABASE Open
blocks MDEV-25921 Implement CREATE TABLE...SELECT in on... Open
is blocked by MDEV-24258 Merge dict_sys.mutex into dict_sys.latch Closed
Duplicate
is duplicated by MDEV-26490 Assertion `!srv_read_only_mode' faile... Closed
Problem/Incident
causes MDEV-26657 SHOW ENGINE INNODB STATUS displays un... Closed
causes MDEV-26772 InnoDB DDL fails with DUPLICATE KEY e... Closed
causes MDEV-26879 innodb_evict_tables_on_commit_debug=o... Closed
causes MDEV-27069 heap-use-after-free in dict_stats_rec... Closed
causes MDEV-27469 Assertion `!trx->read_only' failed in... Closed
causes MDEV-27805 tpcc workload shows regression with M... Closed
causes MDEV-27909 InnoDB: Failing assertion: state == T... Closed
is caused by MDEV-25506 Atomic DDL: .frm file is removed and ... Closed
Relates
relates to MDEV-19505 Do not hold mutex while calling que_g... Closed
relates to MDEV-23484 Rollback unnecessarily acquires dict_... Closed
relates to MDEV-23670 Crash during OPTIMIZE TABLE mysql.inn... Closed
relates to MDEV-31979 Assertion `!internal' failed in void ... Open
relates to MDEV-24579 Error table->get_ref_count() after up... Closed

 Description   

In the third and last part of MDEV-25506, operations that delete .ibd files were refactored so that the deletion would take place after transaction commit.

As part of that refactoring, the lock conflict handling of internal transactions was revised so that if the data dictionary is being locked, any conflict would result in an immediate lock wait timeout error. Such lock conflicts should only be able to exist on the InnoDB persistent statistics tables or some internal tables that are related to the InnoDB FULLTEXT INDEX implementation.

We should try to abandon the concept of 'dictionary transaction' and refactor the InnoDB internal SQL parser so that all table handles will be looked up before the parser is invoked. In this way, no data dictionary latch will be required during the execution, and lock waits can be handled in the normal fashion, without fearing any server hang (such as MDEV-15020).

Making all transactions equal will remove the need to use separate internal transactions for InnoDB DDL operations.

Note: Before MDEV-24589 and MDEV-25506, some InnoDB DDL operations were neither atomic nor rollback-safe, and mixing DDL and DML in one transaction would have been out of the question.



 Comments   
Comment by Marko Mäkelä [ 2021-07-27 ]

After all, it can be useful to keep the concept of ‘dictionary transaction’, so that such transactions can be rolled back during an early phase of startup. Other transactions that do not hold locks on dictionary tables can be rolled back in the background while the server is already accepting connections.

What really needs to change is that the current thread must not hold any dict_sys latch when attempting to acquire an InnoDB table or record lock. We must remove any table lookup from pars_sql() and always pass the table names via pars_bound_id_t.

Comment by Marko Mäkelä [ 2021-07-27 ]

As part of this fix, we may reduce the use of trx_t::dict_operation_lock_mode. Also MDEV-23484 would reduce the use of the field. Maybe the field can be removed altogether.

Comment by Marko Mäkelä [ 2021-08-27 ]

Random failures of the test parts.partition_special_innodb were causing some headache until I realized that

--error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 ADD PARTITION PARTITIONS 2;

will invoke ha_innobase::delete_table() without holding MDL_EXCLUSIVE. Therefore, background tasks that might access the newly created partitions must explicitly be disabled in this case; we cannot rely on MDL.

Comment by Marko Mäkelä [ 2021-08-31 ]

We also removed dict_table_t::stats_bg_flag with proper use of MDL. The background tasks that would update persistent statistics for InnoDB tables or defragment them will acquire MDL on the table name, which will ensure that no DDL may be executed concurrently on the tables.

The refactored waiting logic should essentially fix MDEV-15020.

Comment by Marko Mäkelä [ 2021-09-24 ]

For the record, I just got this test to fail once on a 10.5-based branch. I had added the test into my local development tree months ago, and only now it failed for the first time:

--source include/have_innodb.inc
 
CREATE TABLE t (a INT) ENGINE=InnoDB STATS_PERSISTENT=1;
--source include/restart_mysqld.inc
connect (con,localhost,root);
send ALTER TABLE mysql.innodb_index_stats FORCE, ALGORITHM=INPLACE;
connection default;
SELECT * FROM t;
connection con;
reap;
connection default;
DROP TABLE t;

The failure looked like this:

10.5 7d360060cb6ea2558820b343ef77d59584b0b805

mysqltest: At line 8: query 'SELECT * FROM t' failed: 2013: Lost connection to MySQL server during query
mariadbd: /mariadb/10.5m/storage/innobase/handler/handler0alter.cc:10008: bool commit_try_rebuild(Alter_inplace_info *, ha_innobase_inplace_ctx *, TABLE *, const TABLE *, trx_t *, const char *): Assertion `user_table->get_ref_count() == 1' failed.

Generated at Thu Feb 08 09:41:24 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.