MDEV-14637: Fix hang due to DDL with FOREIGN KEY
When MySQL 5.7.1 introduced WL#6326 to reduce contention on the non-leaf levels of B-trees, it introduced a new rw-lock mode SX (not conflicting with S, but conflicting with SX and X) and new rules to go with it. A thread that is holding an dict_index_t::lock aka index->lock in SX mode is permitted to acquire non-leaf buf_block_t::lock aka block->lock X or SX mode, in monotonically descending order. That is, once the thread has acquired a block->lock, it is not allowed to acquire a lock on its parent or grandparent pages. Such arbitrary-order access is only allowed when the thread acquired the index->lock in X mode upfront. A customer encountered a repeatable hang when loading a dump into InnoDB while using multiple innodb_purge_threads (default: 4). The dump makes very heavy use of FOREIGN KEY constraints. By luck, it happened so that two purge worker threads (srv_worker_thread) deadlocked with each other. Both were operating on the index FOR_REF of the InnoDB internal table SYS_FOREIGN. One of them was legitimately holding index->lock S-latch and the root block->lock S-latch. The other had acquired index->lock SX-latch, root block->lock SX-latch, and a bunch of other latches, including the fil_space_t::latch for freeing some blocks and some leaf page latches. This other thread was inside 2 nested calls to btr_compress() and it was trying to reacquire the root block->lock in X mode, violating the WL#6326 protocol. This violation led to a deadlock, because while S is compatible with SX and a thread can upgrade an SX lock to X when there are no conflicting requests, in this case there was a conflicting S lock held by the other purge worker thread. During this deadlock, both threads are holding dict_operation_lock S-latch, which would block any subsequent DDL statements, such as CREATE TABLE. The tables SYS_FOREIGN and SYS_FOREIGN_COLS are special in that they define key columns of the type VARCHAR(0), created using the InnoDB internal SQL parser. Because InnoDB does not internally enforce the maximum length of columns, it would happily write more than 0 bytes to these columns. This caused a miscalculation of node_ptr_max_size. btr_cur_will_modify_tree(): Clean up some code. (No functional change.) btr_node_ptr_max_size(): Renamed from dict_index_node_ptr_max_size(). Use a more realistic maximum size for SYS_FOREIGN and SYS_FOREIGN_COLS. btr_cur_pessimistic_delete(): Refrain from merging pages if it is not safe. This work is based on the following MySQL 5.7.23 fix: commit 58dcf0b4a4165ed59de94a9a1e7d8c954f733726 Author: Aakanksha Verma <aakanksha.verma@oracle.com> Date: Wed May 9 18:54:03 2018 +0530 BUG#26225783 MYSQL CRASH ON CREATE TABLE (REPRODUCEABLE) -> INNODB: A LONG SEMAPHORE WAIT
Showing
Please register or sign in to comment