mariadb/mysql-test/main/mdl.result
Sergey Vojtovich 18985d8471 MDEV-19749 - MDL scalability regression after backup locks
Statements that intend to modify data have to acquire protection
against ongoing backup. Prior to backup locks, protection against
FTWRL was acquired in form of 2 shared metadata locks of GLOBAL
(global read lock) and COMMIT namespaces. These two namespaces
were separate entities, they didn't share data structures and
locking primitives. And thus they were separate contention
points.

With backup locks, introduced by 7a9dfdd, these namespaces were
combined into a single BACKUP namespace. It became a single
contention point, which doubled load on BACKUP namespace data
structures and locking primitives compared to GLOBAL and COMMIT
namespaces. In other words system throughput has halved.

MDL fast lanes solve this problem by allowing multiple contention
points for single MDL_lock. Fast lane is scalable multi-instance
registry for leightweight locks. Internally it is just a list of
granted tickets, close counter and a mutex.

Number of fast lanes (or contention points) is defined by the
metadata_locks_instances system variable. Value of 1 disables fast
lanes and lock requests are served by conventional MDL_lock data
structures.

Since fast lanes allow arbitrary number of contention points, they
outperform pre-backup locks GLOBAL and COMMIT.

Fast lanes are enabled only for BACKUP namespace. Support for other
namespaces is to be implemented separately.

Lock types are divided in 2 categories: lightweight and heavyweight.

Lightweight lock types represent DML: MDL_BACKUP_DML,
MDL_BACKUP_TRANS_DML, MDL_BACKUP_SYS_DML, MDL_BACKUP_DDL,
MDL_BACKUP_ALTER_COPY, MDL_BACKUP_COMMIT. They are fully compatible
with each other. Normally served by corresponding fast lane, which is
determined by thread_id % metadata_locks_instances.

Heavyweight lock types represent ongoing backup: MDL_BACKUP_START,
MDL_BACKUP_FLUSH, MDL_BACKUP_WAIT_FLUSH, MDL_BACKUP_WAIT_DDL,
MDL_BACKUP_WAIT_COMMIT, MDL_BACKUP_FTWRL1, MDL_BACKUP_FTWRL2,
MDL_BACKUP_BLOCK_DDL. These locks are always served by conventional
MDL_lock data structures. Whenever such lock is requested, fast
lanes are closed and all tickets registered in fast lanes are
moved to conventional MDL_lock data structures. Until such locks
are released or aborted, lightweight lock requests are served by
conventional MDL_lock data structures.

Strictly speaking moving tickets from fast lanes to conventional
MDL_lock data structures is not required. But it allows to reduce
complexity and keep intact methods like: MDL_lock::visit_subgraph(),
MDL_lock::notify_conflicting_locks(), MDL_lock::reschedule_waiters(),
MDL_lock::can_grant_lock().

It is not even required to register tickets in fast lanes. They
can be implemented basing on an atomic variable that holds two
counters: granted lightweight locks and granted/waiting heavyweight
locks. Similarly to MySQL solution, which roughly speaking has
"single atomic fast lane". However it appears to be it won't bring
any better performance, while code complexity is going to be much
higher.
2025-07-15 23:19:06 +04:00

144 lines
4.6 KiB
Text

#
# MDEV-12882 - Assertion `mdl_ticket->m_type == MDL_SHARED_UPGRADABLE ||
# mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
# mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE ||
# mdl_ticket->m_type == MDL_SHARED_READ'
# failed in MDL_context::upgrade_shared_lock
#
CREATE TABLE t1(a INT) ENGINE=InnoDB;
CREATE TABLE t3(a INT) ENGINE=myisam;
LOCK TABLES t1 WRITE CONCURRENT, t1 AS t2 READ;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_TRANS_DML Backup lock
MDL_SHARED_NO_READ_WRITE Table metadata lock test t1
UNLOCK TABLES;
LOCK TABLES t1 AS t2 READ, t1 WRITE CONCURRENT;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_TRANS_DML Backup lock
MDL_SHARED_WRITE Table metadata lock test t1
MDL_SHARED_READ_ONLY Table metadata lock test t1
UNLOCK TABLES;
LOCK TABLES t1 WRITE CONCURRENT, t3 WRITE;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_DDL Backup lock
MDL_BACKUP_DML Backup lock
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_SHARED_NO_READ_WRITE Table metadata lock test t3
MDL_SHARED_WRITE Table metadata lock test t1
UNLOCK TABLES;
LOCK TABLES t3 WRITE, t1 WRITE CONCURRENT;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_DDL Backup lock
MDL_BACKUP_DML Backup lock
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_SHARED_NO_READ_WRITE Table metadata lock test t3
MDL_SHARED_WRITE Table metadata lock test t1
UNLOCK TABLES;
LOCK TABLES t1 WRITE, mysql.user WRITE;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_DDL Backup lock
MDL_SHARED_NO_READ_WRITE Table metadata lock mysql user
MDL_SHARED_NO_READ_WRITE Table metadata lock test t1
MDL_INTENTION_EXCLUSIVE Schema metadata lock mysql
MDL_SHARED_NO_READ_WRITE Table metadata lock mysql global_priv
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
UNLOCK TABLES;
LOCK TABLES mysql.general_log WRITE;
ERROR HY000: You can't use locks with log tables
LOCK TABLES t1 WRITE,information_schema.tables READ;
UNLOCK TABLES;
DROP TABLE t1,t3;
#
# Check MDL locks taken for different kind of tables by open
#
CREATE TABLE t1(a INT) stats_persistent=0, ENGINE=InnoDB;
CREATE TABLE t3(a INT) ENGINE=myisam;
connect purge_control,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect locker,localhost,root,,;
connection default;
FLUSH TABLES WITH READ LOCK;
connection locker;
insert into t1 values (1);
connection default;
connection default;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_FTWRL2 Backup lock
MDL_SHARED_WRITE Table metadata lock test t1
unlock tables;
connection locker;
unlock tables;
connection default;
FLUSH TABLES WITH READ LOCK;
connection locker;
insert into t3 values (2);
connection default;
connection default;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_FTWRL2 Backup lock
MDL_SHARED_WRITE Table metadata lock test t3
unlock tables;
connection locker;
unlock tables;
disconnect purge_control;
connection default;
disconnect locker;
DROP TABLE t1,t3;
#
# MDEV-28820 MyISAM wrong server status flags
#
create table t1 (a int);
set autocommit=0;
select @@in_transaction;
@@in_transaction
0
select * from t1;
a
select @@in_transaction;
@@in_transaction
0
connect foo,localhost,root;
drop table t1;
connection default;
set autocommit=1;
create table t1 (a int);
create table t2 (b int) engine=innodb;
set autocommit=0;
select @@in_transaction;
@@in_transaction
0
select * from t2;
b
select @@in_transaction;
@@in_transaction
1
select * from t1;
a
connection foo;
drop table t1;
connection default;
select * from t1;
a
commit;
connection foo;
disconnect foo;
connection default;
set autocommit=default;
drop table t2;
#
# End of 10.4 tests
#