Skip to content

Commit 9573037

Browse files
committed
MDEV-30165 X-lock on supremum for prepared transaction for RR
trx_t::set_skip_lock_inheritance() must be invoked at the very beginning of lock_release_on_prepare(). Currently trx_t::set_skip_lock_inheritance() is invoked at the end of lock_release_on_prepare() when lock_sys and trx are released, and there can be a case when locks on prepare are released, but "not inherit gap locks" bit has not yet been set, and page split inherits lock to supremum. Also reset supremum bit and rebuild waiting queue when XA is prepared. Reviewed by: Marko Mäkelä
1 parent 8513f8f commit 9573037

File tree

5 files changed

+132
-29
lines changed

5 files changed

+132
-29
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CREATE TABLE t (
2+
`a` INT NOT NULL,
3+
PRIMARY KEY (`a`)
4+
) ENGINE=InnoDB;
5+
INSERT INTO t VALUES(10);
6+
INSERT INTO t VALUES(20);
7+
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
8+
XA START '1';
9+
SELECT * FROM t WHERE a > 20 FOR UPDATE;
10+
a
11+
INSERT INTO t VALUES(40);
12+
XA END '1';
13+
XA PREPARE '1';
14+
connect con1,localhost,root;
15+
SET innodb_lock_wait_timeout=1;
16+
INSERT INTO t VALUES(50);
17+
disconnect con1;
18+
connection default;
19+
XA COMMIT '1';
20+
DROP TABLE t;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--source include/have_innodb.inc
2+
--source include/count_sessions.inc
3+
4+
CREATE TABLE t (
5+
`a` INT NOT NULL,
6+
PRIMARY KEY (`a`)
7+
) ENGINE=InnoDB;
8+
9+
10+
INSERT INTO t VALUES(10);
11+
INSERT INTO t VALUES(20);
12+
13+
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
14+
XA START '1';
15+
SELECT * FROM t WHERE a > 20 FOR UPDATE;
16+
INSERT INTO t VALUES(40);
17+
XA END '1';
18+
XA PREPARE '1';
19+
20+
connect (con1,localhost,root);
21+
SET innodb_lock_wait_timeout=1;
22+
# This will be finished with lock wait timeout error if XA PREPARE did not
23+
# reset lock on supremum
24+
INSERT INTO t VALUES(50);
25+
--disconnect con1
26+
27+
--connection default
28+
XA COMMIT '1';
29+
DROP TABLE t;
30+
31+
--source include/wait_until_count_sessions.inc

storage/innobase/include/lock0priv.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,15 @@ lock_rec_get_next_const(
560560
ulint heap_no,/*!< in: heap number of the record */
561561
const lock_t* lock); /*!< in: lock */
562562

563+
/** Gets the first explicit lock request on a record.
564+
@param hash hash chain the lock on
565+
@param page_id page id containing the record
566+
@param heap_no heap number of the record
567+
@return first lock, NULL if none exists */
568+
UNIV_INLINE
569+
lock_t *lock_rec_get_first(hash_table_t *hash, const page_id_t page_id,
570+
ulint heap_no);
571+
563572
/*********************************************************************//**
564573
Gets the first explicit lock request on a record.
565574
@return first lock, NULL if none exists */

storage/innobase/include/lock0priv.inl

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ lock_rec_get_next_const(
154154
return(lock_rec_get_next(heap_no, (lock_t*) lock));
155155
}
156156

157+
/** Gets the first explicit lock request on a record.
158+
@param hash hash chain the lock on
159+
@param page_id page id containing the record
160+
@param heap_no heap number of the record
161+
@return first lock, NULL if none exists */
162+
UNIV_INLINE
163+
lock_t *lock_rec_get_first(hash_table_t *hash, const page_id_t page_id,
164+
ulint heap_no)
165+
{
166+
for (lock_t *lock= lock_sys.get_first(*hash, page_id); lock;
167+
lock= lock_rec_get_next_on_page(lock))
168+
if (lock_rec_get_nth_bit(lock, heap_no))
169+
return lock;
170+
return nullptr;
171+
}
172+
157173
/*********************************************************************//**
158174
Gets the first explicit lock request on a record.
159175
@return first lock, NULL if none exists */
@@ -165,11 +181,7 @@ lock_rec_get_first(
165181
const buf_block_t* block, /*!< in: block containing the record */
166182
ulint heap_no)/*!< in: heap number of the record */
167183
{
168-
for (lock_t *lock= lock_sys.get_first(*hash, block->page.id());
169-
lock; lock= lock_rec_get_next_on_page(lock))
170-
if (lock_rec_get_nth_bit(lock, heap_no))
171-
return lock;
172-
return nullptr;
184+
return lock_rec_get_first(hash, block->page.id(), heap_no);
173185
}
174186

175187
/*********************************************************************//**

storage/innobase/lock/lock0lock.cc

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4121,6 +4121,39 @@ lock_grant_and_move_on_rec(
41214121
}
41224122
}
41234123

4124+
/** Rebuild waiting queue after first_lock for heap_no. The queue is rebuilt
4125+
close to the way lock_rec_dequeue_from_page() does it.
4126+
@param trx transaction that has set a lock, which caused the queue
4127+
rebuild
4128+
@param first_lock the lock after which waiting queue will be rebuilt
4129+
@param heap_no heap no of the record for which waiting queue to rebuild */
4130+
static void lock_rec_rebuild_waiting_queue(trx_t *trx, lock_t *first_lock,
4131+
ulint heap_no)
4132+
{
4133+
ut_ad(lock_mutex_own());
4134+
ut_ad(trx_mutex_own(trx));
4135+
if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
4136+
thd_is_replication_slave_thread(trx->mysql_thd))
4137+
{
4138+
/* Check if we can now grant waiting lock requests */
4139+
for (lock_t *lock= first_lock; lock != NULL;
4140+
lock= lock_rec_get_next(heap_no, lock))
4141+
{
4142+
if (!lock_get_wait(lock))
4143+
continue;
4144+
const lock_t *c= lock_rec_has_to_wait_in_queue(lock);
4145+
if (!c)
4146+
{
4147+
/* Grant the lock */
4148+
ut_ad(trx != lock->trx);
4149+
lock_grant(lock);
4150+
}
4151+
}
4152+
}
4153+
else
4154+
lock_grant_and_move_on_rec(first_lock, heap_no);
4155+
}
4156+
41244157
/*************************************************************//**
41254158
Removes a granted record lock of a transaction from the queue and grants
41264159
locks to other transactions waiting in the queue if they now are entitled
@@ -4181,28 +4214,7 @@ lock_rec_unlock(
41814214
released:
41824215
ut_a(!lock_get_wait(lock));
41834216
lock_rec_reset_nth_bit(lock, heap_no);
4184-
4185-
if (innodb_lock_schedule_algorithm
4186-
== INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
4187-
thd_is_replication_slave_thread(lock->trx->mysql_thd)) {
4188-
4189-
/* Check if we can now grant waiting lock requests */
4190-
4191-
for (lock = first_lock; lock != NULL;
4192-
lock = lock_rec_get_next(heap_no, lock)) {
4193-
if (!lock_get_wait(lock)) {
4194-
continue;
4195-
}
4196-
const lock_t* c = lock_rec_has_to_wait_in_queue(lock);
4197-
if (!c) {
4198-
/* Grant the lock */
4199-
ut_ad(trx != lock->trx);
4200-
lock_grant(lock);
4201-
}
4202-
}
4203-
} else {
4204-
lock_grant_and_move_on_rec(first_lock, heap_no);
4205-
}
4217+
lock_rec_rebuild_waiting_queue(trx, first_lock, heap_no);
42064218

42074219
lock_mutex_exit();
42084220
trx_mutex_exit(trx);
@@ -4334,10 +4346,28 @@ void lock_release(trx_t* trx)
43344346
#endif
43354347
}
43364348

4349+
/** Reset lock bit for supremum and rebuild waiting queue.
4350+
@param lock the lock with supemum bit set */
4351+
static void lock_rec_unlock_supremum(lock_t *lock)
4352+
{
4353+
ut_ad(lock_rec_get_nth_bit(lock, PAGE_HEAP_NO_SUPREMUM));
4354+
ut_ad(lock_mutex_own());
4355+
ut_ad(lock_get_type_low(lock) == LOCK_REC);
4356+
trx_mutex_enter(lock->trx);
4357+
lock_t *first_lock=
4358+
lock_rec_get_first(&lock_sys.rec_hash, lock->un_member.rec_lock.page_id,
4359+
PAGE_HEAP_NO_SUPREMUM);
4360+
lock_rec_reset_nth_bit(lock, PAGE_HEAP_NO_SUPREMUM);
4361+
lock_rec_rebuild_waiting_queue(lock->trx, first_lock, PAGE_HEAP_NO_SUPREMUM);
4362+
trx_mutex_exit(lock->trx);
4363+
}
4364+
43374365
/** Release non-exclusive locks on XA PREPARE,
43384366
and release possible other transactions waiting because of these locks. */
43394367
void lock_release_on_prepare(trx_t *trx)
43404368
{
4369+
trx->set_skip_lock_inheritance();
4370+
43414371
ulint count= 0;
43424372
lock_mutex_enter();
43434373
ut_ad(!trx_mutex_own(trx));
@@ -4349,8 +4379,10 @@ void lock_release_on_prepare(trx_t *trx)
43494379
if (lock_get_type_low(lock) == LOCK_REC)
43504380
{
43514381
ut_ad(!lock->index->table->is_temporary());
4352-
if (lock_rec_get_gap(lock) || lock_get_mode(lock) != LOCK_X)
4382+
if ((lock->type_mode & (LOCK_MODE_MASK | LOCK_GAP)) != LOCK_X)
43534383
lock_rec_dequeue_from_page(lock);
4384+
else if (lock_rec_get_nth_bit(lock, PAGE_HEAP_NO_SUPREMUM))
4385+
lock_rec_unlock_supremum(lock);
43544386
else
43554387
{
43564388
ut_ad(trx->dict_operation ||
@@ -4397,7 +4429,6 @@ void lock_release_on_prepare(trx_t *trx)
43974429

43984430
lock_mutex_exit();
43994431

4400-
trx->set_skip_lock_inheritance();
44014432
}
44024433

44034434
/* True if a lock mode is S or X */

0 commit comments

Comments
 (0)