-
Notifications
You must be signed in to change notification settings - Fork 470
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support SELECT FOR UPDATE SKIP LOCKED / NOWAIT
Summary: SKIP LOCKED is very similar with TTL / ICP that we need to skip locked rows and proceed to the next one. 5.6 didn't have correct implementation even for TTL in many cases (index_last / index_first / index_read_map_impl) but in 8.0 Manuel's refactoring made things much easier. Note that when skipping locked rows, we also skip rows that can cause deadlock and snapshot conflict in the spirit of SKIP LOCKED in order to lock the rows that we can update later. NOWAIT is quite straight-forward - just set timeout to 0 and restore it after we are done. We still return ER_LOCK_WAIT_TIMEOUT instead of ER_LOCK_NOWAIT (in InnoDB) if we couldn't take the lock and personally I don't think it makes much difference. Reviewed By: lth Differential Revision: D28922476 fbshipit-source-id: 1bb698a2bda
- Loading branch information
1 parent
ab824f9
commit 15d6968
Showing
10 changed files
with
511 additions
and
360 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
# | ||
# wl#8919 Implement NOWAIT and SKIP LOCKED | ||
# | ||
|
||
# needed by start_transaction_high_prio.inc | ||
--source include/have_debug.inc | ||
|
||
--source include/count_sessions.inc | ||
|
||
connect (con1,localhost,root,,); | ||
if ($engine=="INNODB") | ||
{ | ||
SET SESSION innodb_lock_wait_timeout=1; | ||
} | ||
if ($engine=="ROCKSDB") | ||
{ | ||
SET SESSION rocksdb_lock_wait_timeout=1; | ||
} | ||
|
||
connection default; | ||
if ($engine=="INNODB") | ||
{ | ||
SET SESSION innodb_lock_wait_timeout=1; | ||
} | ||
if ($engine=="ROCKSDB") | ||
{ | ||
SET SESSION rocksdb_lock_wait_timeout=1; | ||
} | ||
|
||
--echo # Case 1: Test primary index | ||
eval CREATE TABLE t1( | ||
seat_id INT, | ||
state INT, | ||
PRIMARY KEY(seat_id) | ||
) ENGINE=$engine; | ||
|
||
INSERT INTO t1 VALUES(1,0), (2,0), (3,0), (4,0); | ||
|
||
BEGIN; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE; | ||
|
||
connection con1; | ||
BEGIN; | ||
|
||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 LOCK IN SHARE MODE; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE; | ||
|
||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE SKIP LOCKED; | ||
|
||
--error ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE seat_id > 0 LIMIT 2 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE seat_id > 0 LIMIT 2 FOR UPDATE SKIP LOCKED; | ||
|
||
COMMIT; | ||
|
||
connection default; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE; | ||
|
||
connection con1; | ||
--error ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 LOCK IN SHARE MODE; | ||
|
||
--error ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR SHARE SKIP LOCKED; | ||
|
||
--error ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE seat_id > 0 LIMIT 2 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE seat_id > 0 LIMIT 2 FOR UPDATE SKIP LOCKED; | ||
|
||
COMMIT; | ||
|
||
connection default; | ||
COMMIT; | ||
|
||
DROP TABLE t1; | ||
|
||
--echo # Case 2: Test primary index & secondary index | ||
eval CREATE TABLE t1( | ||
seat_id INT, | ||
row_id INT, | ||
state INT, | ||
PRIMARY KEY(seat_id), | ||
KEY(row_id) | ||
) ENGINE=$engine; | ||
|
||
INSERT INTO t1 VALUES(1,1,0), (2,1,0), (3,2,0), (4,2,0); | ||
|
||
--echo # Test secondary key | ||
# Case 2a: secondary blocks secondary/primary | ||
BEGIN; | ||
SELECT * FROM t1 WHERE state = 0 AND row_id = 1 LIMIT 1 FOR UPDATE NOWAIT; | ||
|
||
connection con1; | ||
BEGIN; | ||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 AND row_id = 1 LIMIT 1 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 AND row_id = 1 LIMIT 1 FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 AND row_id > 0 LIMIT 1 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 AND row_id > 0 LIMIT 1 FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 FOR UPDATE SKIP LOCKED; | ||
|
||
COMMIT; | ||
|
||
connection default; | ||
COMMIT; | ||
|
||
# Case 2b: primary blocks secondary/primary | ||
BEGIN; | ||
SELECT * FROM t1 WHERE seat_id = 1 FOR UPDATE NOWAIT; | ||
|
||
connection con1; | ||
BEGIN; | ||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 AND row_id = 1 LIMIT 1 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 AND row_id = 1 LIMIT 1 FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT * FROM t1 WHERE state = 0 FOR UPDATE NOWAIT; | ||
SELECT * FROM t1 WHERE state = 0 FOR UPDATE SKIP LOCKED; | ||
|
||
COMMIT; | ||
|
||
connection default; | ||
COMMIT; | ||
|
||
DROP TABLE t1; | ||
|
||
if ($engine=='INNODB') | ||
{ | ||
--echo # Case 3: Test primary index & spatial index | ||
eval CREATE TABLE t1( | ||
seat_id INT, | ||
pos POINT NOT NULL SRID 0, | ||
state INT, | ||
PRIMARY KEY(seat_id), | ||
SPATIAL KEY(pos) | ||
) ENGINE=$engine; | ||
|
||
INSERT INTO t1 VALUES | ||
(1,ST_PointFromText('POINT(1 0)'),0), | ||
(2,ST_PointFromText('POINT(1 1)'),0), | ||
(3,ST_PointFromText('POINT(2 0)'),0), | ||
(4,ST_PointFromText('POINT(2 1)'),0), | ||
(5,ST_PointFromText('POINT(3 0)'),0), | ||
(6,ST_PointFromText('POINT(3 1)'),0); | ||
|
||
# Case 3a: secondary blocks secondary/primary | ||
BEGIN; | ||
SET @g = ST_GeomFromText('POLYGON((0 0,0 2,2 2,0 2,0 0))'); | ||
# the first 4 records in the rtree index page are locked | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 FORCE INDEX (pos) | ||
WHERE state = 0 AND MBRWithin(pos, @g) FOR UPDATE NOWAIT; | ||
|
||
connection con1; | ||
BEGIN; | ||
SET @g = ST_GeomFromText('POLYGON((0 0,0 4,4 4,0 4,0 0))'); | ||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 FORCE INDEX (pos) | ||
WHERE state = 0 AND MBRWithin(pos, @g) FOR UPDATE NOWAIT; | ||
|
||
SELECT seat_id, state, ST_AsText(pos) FROM t1 FORCE INDEX (pos) | ||
WHERE state = 0 AND MBRWithin(pos, @g) FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 | ||
WHERE state = 0 FOR UPDATE NOWAIT; | ||
|
||
SELECT seat_id, state, ST_AsText(pos) FROM t1 | ||
WHERE state = 0 FOR UPDATE SKIP LOCKED; | ||
|
||
COMMIT; | ||
|
||
connection default; | ||
COMMIT; | ||
|
||
# Case 3b: primary blockes secondary/primary | ||
connection con1; | ||
SET @g = ST_GeomFromText('POLYGON((0 0,0 3,3 3,0 3,0 0))'); | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 FORCE INDEX (pos) | ||
WHERE state = 0 AND MBRWithin(pos, @g) FOR UPDATE; | ||
|
||
connection default; | ||
BEGIN; | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 | ||
WHERE seat_id = 4 FOR UPDATE NOWAIT; | ||
|
||
connection con1; | ||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 FORCE INDEX (pos) | ||
WHERE state = 0 AND MBRWithin(pos, @g) FOR UPDATE NOWAIT; | ||
|
||
SELECT seat_id, state, ST_AsText(pos) FROM t1 FORCE INDEX (pos) | ||
WHERE state = 0 AND MBRWithin(pos, @g) FOR UPDATE SKIP LOCKED; | ||
|
||
--error ER_LOCK_NOWAIT, ER_LOCK_WAIT_TIMEOUT | ||
SELECT seat_id, state, ST_AsText(pos) FROM t1 | ||
WHERE state = 0 FOR UPDATE NOWAIT; | ||
|
||
SELECT seat_id, state, ST_AsText(pos) FROM t1 | ||
WHERE state = 0 FOR UPDATE SKIP LOCKED; | ||
|
||
connection default; | ||
COMMIT; | ||
|
||
DROP TABLE t1; | ||
|
||
--echo # Case 4: Test high priority transaction | ||
eval CREATE TABLE t1( | ||
seat_id INT, | ||
state INT, | ||
PRIMARY KEY(seat_id) | ||
) ENGINE=$engine; | ||
|
||
INSERT INTO t1 VALUES(1,0), (2,0), (3,0), (4,0); | ||
|
||
BEGIN; | ||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE; | ||
|
||
connection con1; | ||
|
||
--source include/start_transaction_high_prio.inc | ||
|
||
SELECT * FROM t1 WHERE seat_id = 1 AND state = 0 FOR UPDATE NOWAIT; | ||
|
||
SELECT * FROM t1 WHERE state = 0 LIMIT 2 FOR UPDATE SKIP LOCKED; | ||
|
||
COMMIT; | ||
|
||
connection default; | ||
--error ER_ERROR_DURING_COMMIT | ||
COMMIT; | ||
ROLLBACK; | ||
|
||
DROP TABLE t1; | ||
} | ||
|
||
disconnect con1; | ||
|
||
--source include/wait_until_count_sessions.inc |
Oops, something went wrong.