mirror of
https://github.com/postgres/postgres.git
synced 2025-10-18 04:29:09 +03:00
Implement SKIP LOCKED for row-level locks
This clause changes the behavior of SELECT locking clauses in the presence of locked rows: instead of causing a process to block waiting for the locks held by other processes (or raise an error, with NOWAIT), SKIP LOCKED makes the new reader skip over such rows. While this is not appropriate behavior for general purposes, there are some cases in which it is useful, such as queue-like tables. Catalog version bumped because this patch changes the representation of stored rules. Reviewed by Craig Ringer (based on a previous attempt at an implementation by Simon Riggs, who also provided input on the syntax used in the current patch), David Rowley, and Álvaro Herrera. Author: Thomas Munro
This commit is contained in:
@@ -27,4 +27,9 @@ step "s2d" { UPDATE foo SET data = data; }
|
||||
step "s2e" { SELECT pg_advisory_unlock(0); }
|
||||
step "s2f" { COMMIT; }
|
||||
|
||||
# s1 takes a snapshot but then waits on an advisory lock, then s2
|
||||
# updates the row in one transaction, then again in another without
|
||||
# committing, before allowing s1 to proceed to try to lock a row;
|
||||
# because it has a snapshot that sees the older version, we reach the
|
||||
# waiting code in EvalPlanQualFetch which ereports when in NOWAIT mode.
|
||||
permutation "s2a" "s1a" "s2b" "s2c" "s2d" "s2e" "s1b" "s2f"
|
||||
|
41
src/test/isolation/specs/skip-locked-2.spec
Normal file
41
src/test/isolation/specs/skip-locked-2.spec
Normal file
@@ -0,0 +1,41 @@
|
||||
# Test SKIP LOCKED with multixact locks.
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE queue (
|
||||
id int PRIMARY KEY,
|
||||
data text NOT NULL,
|
||||
status text NOT NULL
|
||||
);
|
||||
INSERT INTO queue VALUES (1, 'foo', 'NEW'), (2, 'bar', 'NEW');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE queue;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup { BEGIN; }
|
||||
step "s1a" { SELECT * FROM queue ORDER BY id FOR SHARE SKIP LOCKED LIMIT 1; }
|
||||
step "s1b" { COMMIT; }
|
||||
|
||||
session "s2"
|
||||
setup { BEGIN; }
|
||||
step "s2a" { SELECT * FROM queue ORDER BY id FOR SHARE SKIP LOCKED LIMIT 1; }
|
||||
step "s2b" { SELECT * FROM queue ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1; }
|
||||
step "s2c" { COMMIT; }
|
||||
|
||||
# s1 and s2 both get SHARE lock, creating a multixact lock, then s2
|
||||
# tries to update to UPDATE but skips the record because it can't
|
||||
# acquire a multixact lock
|
||||
permutation "s1a" "s2a" "s2b" "s1b" "s2c"
|
||||
|
||||
# the same but with the SHARE locks acquired in a different order, so
|
||||
# s2 again skips because it can't acquired a multixact lock
|
||||
permutation "s2a" "s1a" "s2b" "s1b" "s2c"
|
||||
|
||||
# s2 acquires SHARE then UPDATE, then s1 tries to acquire SHARE but
|
||||
# can't so skips the first record because it can't acquire a regular
|
||||
# lock
|
||||
permutation "s2a" "s2b" "s1a" "s1b" "s2c"
|
36
src/test/isolation/specs/skip-locked-3.spec
Normal file
36
src/test/isolation/specs/skip-locked-3.spec
Normal file
@@ -0,0 +1,36 @@
|
||||
# Test SKIP LOCKED with tuple locks.
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE queue (
|
||||
id int PRIMARY KEY,
|
||||
data text NOT NULL,
|
||||
status text NOT NULL
|
||||
);
|
||||
INSERT INTO queue VALUES (1, 'foo', 'NEW'), (2, 'bar', 'NEW');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE queue;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup { BEGIN; }
|
||||
step "s1a" { SELECT * FROM queue ORDER BY id FOR UPDATE LIMIT 1; }
|
||||
step "s1b" { COMMIT; }
|
||||
|
||||
session "s2"
|
||||
setup { BEGIN; }
|
||||
step "s2a" { SELECT * FROM queue ORDER BY id FOR UPDATE LIMIT 1; }
|
||||
step "s2b" { COMMIT; }
|
||||
|
||||
session "s3"
|
||||
setup { BEGIN; }
|
||||
step "s3a" { SELECT * FROM queue ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1; }
|
||||
step "s3b" { COMMIT; }
|
||||
|
||||
# s3 skips to the second record because it can't obtain the tuple lock
|
||||
# (s2 holds the tuple lock because it is next in line to obtain the
|
||||
# row lock, and s1 holds the row lock)
|
||||
permutation "s1a" "s2a" "s3a" "s1b" "s2b" "s3b"
|
36
src/test/isolation/specs/skip-locked-4.spec
Normal file
36
src/test/isolation/specs/skip-locked-4.spec
Normal file
@@ -0,0 +1,36 @@
|
||||
# Test SKIP LOCKED with an updated tuple chain.
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE foo (
|
||||
id int PRIMARY KEY,
|
||||
data text NOT NULL
|
||||
);
|
||||
INSERT INTO foo VALUES (1, 'x'), (2, 'x');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE foo;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup { BEGIN; }
|
||||
step "s1a" { SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL ORDER BY id LIMIT 1 FOR UPDATE SKIP LOCKED; }
|
||||
step "s1b" { COMMIT; }
|
||||
|
||||
session "s2"
|
||||
step "s2a" { SELECT pg_advisory_lock(0); }
|
||||
step "s2b" { UPDATE foo SET data = data WHERE id = 1; }
|
||||
step "s2c" { BEGIN; }
|
||||
step "s2d" { UPDATE foo SET data = data WHERE id = 1; }
|
||||
step "s2e" { SELECT pg_advisory_unlock(0); }
|
||||
step "s2f" { COMMIT; }
|
||||
|
||||
# s1 takes a snapshot but then waits on an advisory lock, then s2
|
||||
# updates the row in one transaction, then again in another without
|
||||
# committing, before allowing s1 to proceed to try to lock a row;
|
||||
# because it has a snapshot that sees the older version, we reach the
|
||||
# waiting code in EvalPlanQualFetch which skips rows when in SKIP
|
||||
# LOCKED mode, so s1 sees the second row
|
||||
permutation "s2a" "s1a" "s2b" "s2c" "s2d" "s2e" "s1b" "s2f"
|
28
src/test/isolation/specs/skip-locked.spec
Normal file
28
src/test/isolation/specs/skip-locked.spec
Normal file
@@ -0,0 +1,28 @@
|
||||
# Test SKIP LOCKED when regular row locks can't be acquired.
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE queue (
|
||||
id int PRIMARY KEY,
|
||||
data text NOT NULL,
|
||||
status text NOT NULL
|
||||
);
|
||||
INSERT INTO queue VALUES (1, 'foo', 'NEW'), (2, 'bar', 'NEW');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE queue;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup { BEGIN; }
|
||||
step "s1a" { SELECT * FROM queue ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1; }
|
||||
step "s1b" { SELECT * FROM queue ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1; }
|
||||
step "s1c" { COMMIT; }
|
||||
|
||||
session "s2"
|
||||
setup { BEGIN; }
|
||||
step "s2a" { SELECT * FROM queue ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1; }
|
||||
step "s2b" { SELECT * FROM queue ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1; }
|
||||
step "s2c" { COMMIT; }
|
Reference in New Issue
Block a user