mirror of
https://github.com/MariaDB/server.git
synced 2025-04-28 06:45:23 +03:00
MDEV-28422 Page split breaks a gap lock
btr_insert_into_right_sibling(): Inherit any gap lock from the left sibling to the right sibling before inserting the record to the right sibling and updating the node pointer(s). lock_update_node_pointer(): Update locks in case a node pointer will move. Based on mysql/mysql-server@c7d93c274f
This commit is contained in:
parent
b208030ef5
commit
0806592ac8
27
mysql-test/suite/innodb/r/gap_lock_split.result
Normal file
27
mysql-test/suite/innodb/r/gap_lock_split.result
Normal file
@ -0,0 +1,27 @@
|
||||
SET @save_frequency=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
|
||||
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
||||
CREATE TABLE t1(id INT PRIMARY key, val VARCHAR(16000)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 (id,val) SELECT 2*seq,'x' FROM seq_0_to_1023;
|
||||
connect con1,localhost,root,,;
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT;
|
||||
connection default;
|
||||
DELETE FROM t1 WHERE id=1788;
|
||||
BEGIN;
|
||||
SELECT * FROM t1 WHERE id=1788 FOR UPDATE;
|
||||
id val
|
||||
connection con1;
|
||||
COMMIT;
|
||||
InnoDB 0 transactions not purged
|
||||
connection default;
|
||||
INSERT INTO t1 (id,val) VALUES (1787, REPEAT('x',2000));
|
||||
connection con1;
|
||||
SET innodb_lock_wait_timeout=0;
|
||||
INSERT INTO t1 (id,val) VALUES (1788, 'x');
|
||||
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
|
||||
SELECT * FROM t1 WHERE id=1788 FOR UPDATE;
|
||||
id val
|
||||
disconnect con1;
|
||||
connection default;
|
||||
COMMIT;
|
||||
DROP TABLE t1;
|
||||
SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency;
|
39
mysql-test/suite/innodb/t/gap_lock_split.test
Normal file
39
mysql-test/suite/innodb/t/gap_lock_split.test
Normal file
@ -0,0 +1,39 @@
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
|
||||
SET @save_frequency=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
|
||||
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
||||
|
||||
CREATE TABLE t1(id INT PRIMARY key, val VARCHAR(16000)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 (id,val) SELECT 2*seq,'x' FROM seq_0_to_1023;
|
||||
|
||||
connect(con1,localhost,root,,);
|
||||
# Prevent purge.
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT;
|
||||
connection default;
|
||||
|
||||
DELETE FROM t1 WHERE id=1788;
|
||||
|
||||
BEGIN;
|
||||
# This will return no result, but should acquire a gap lock.
|
||||
SELECT * FROM t1 WHERE id=1788 FOR UPDATE;
|
||||
|
||||
connection con1;
|
||||
COMMIT;
|
||||
source include/wait_all_purged.inc;
|
||||
connection default;
|
||||
|
||||
INSERT INTO t1 (id,val) VALUES (1787, REPEAT('x',2000));
|
||||
|
||||
connection con1;
|
||||
SET innodb_lock_wait_timeout=0;
|
||||
--error ER_LOCK_WAIT_TIMEOUT
|
||||
INSERT INTO t1 (id,val) VALUES (1788, 'x');
|
||||
SELECT * FROM t1 WHERE id=1788 FOR UPDATE;
|
||||
disconnect con1;
|
||||
|
||||
connection default;
|
||||
COMMIT;
|
||||
DROP TABLE t1;
|
||||
SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency;
|
@ -2,7 +2,7 @@
|
||||
|
||||
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2012, Facebook Inc.
|
||||
Copyright (c) 2014, 2021, MariaDB Corporation.
|
||||
Copyright (c) 2014, 2022, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -2688,8 +2688,8 @@ btr_insert_into_right_sibling(
|
||||
max_size = page_get_max_insert_size_after_reorganize(next_page, 1);
|
||||
|
||||
/* Extends gap lock for the next page */
|
||||
if (!dict_table_is_locking_disabled(cursor->index->table)) {
|
||||
lock_update_split_left(next_block, block);
|
||||
if (is_leaf && !dict_table_is_locking_disabled(cursor->index->table)) {
|
||||
lock_update_node_pointer(block, next_block);
|
||||
}
|
||||
|
||||
rec = page_cur_tuple_insert(
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2017, 2020, MariaDB Corporation.
|
||||
Copyright (c) 1996, 2022, Oracle and/or its affiliates.
|
||||
Copyright (c) 2017, 2022, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -151,6 +151,40 @@ lock_update_copy_and_discard(
|
||||
which copied */
|
||||
const buf_block_t* block); /*!< in: index page;
|
||||
NOT the root! */
|
||||
/** Update gap locks between the last record of the left_block and the
|
||||
first record of the right_block when a record is about to be inserted
|
||||
at the start of the right_block, even though it should "naturally" be
|
||||
inserted as the last record of the left_block according to the
|
||||
current node pointer in the parent page.
|
||||
|
||||
That is, we assume that the lowest common ancestor of the left_block
|
||||
and right_block routes the key of the new record to the left_block,
|
||||
but a heuristic which tries to avoid overflowing left_block has chosen
|
||||
to insert the record into right_block instead. Said ancestor performs
|
||||
this routing by comparing the key of the record to a "split point" -
|
||||
all records greater or equal to than the split point (node pointer)
|
||||
are in right_block, and smaller ones in left_block.
|
||||
The split point may be smaller than the smallest key in right_block.
|
||||
|
||||
The gap between the last record on the left_block and the first record
|
||||
on the right_block is represented as a gap lock attached to the supremum
|
||||
pseudo-record of left_block, and a gap lock attached to the new first
|
||||
record of right_block.
|
||||
|
||||
Thus, inserting the new record, and subsequently adjusting the node
|
||||
pointers in parent pages to values smaller or equal to the new
|
||||
records' key, will mean that gap will be sliced at a different place
|
||||
("moved to the left"): fragment of the 1st gap will now become treated
|
||||
as 2nd. Therefore, we must copy any GRANTED locks from 1st gap to the
|
||||
2nd gap. Any WAITING locks must be of INSERT_INTENTION type (as no
|
||||
other GAP locks ever wait for anything) and can stay at 1st gap, as
|
||||
their only purpose is to notify the requester they can retry
|
||||
insertion, and there's no correctness requirement to avoid waking them
|
||||
up too soon.
|
||||
@param left_block left page
|
||||
@param right_block right page */
|
||||
void lock_update_node_pointer(const buf_block_t *left_block,
|
||||
const buf_block_t *right_block);
|
||||
/*************************************************************//**
|
||||
Updates the lock table when a page is split to the left. */
|
||||
void
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2014, 2021, MariaDB Corporation.
|
||||
Copyright (c) 1996, 2022, Oracle and/or its affiliates.
|
||||
Copyright (c) 2014, 2022, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -3044,6 +3044,17 @@ lock_update_split_right(
|
||||
lock_mutex_exit();
|
||||
}
|
||||
|
||||
void lock_update_node_pointer(const buf_block_t *left_block,
|
||||
const buf_block_t *right_block)
|
||||
{
|
||||
const ulint h= lock_get_min_heap_no(right_block);
|
||||
|
||||
lock_mutex_enter();
|
||||
lock_rec_inherit_to_gap(right_block, left_block,
|
||||
h, PAGE_HEAP_NO_SUPREMUM);
|
||||
lock_mutex_exit();
|
||||
}
|
||||
|
||||
/*************************************************************//**
|
||||
Updates the lock table when a page is merged to the right. */
|
||||
void
|
||||
|
Loading…
x
Reference in New Issue
Block a user