Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b798aa8

Browse files
MMeentpetergeogheganMichail Nikolaev
authored and
Commitfest Bot
committed
Test for IOS/Vacuum race conditions in index AMs
Add regression tests that demonstrate wrong results can occur with index-only scans in GiST and SP-GiST indexes when encountering tuples being removed by a concurrent VACUUM operation. With these tests the index AMs are also expected to not block VACUUM even when they're used inside a cursor. Co-authored-by: Matthias van de Meent <boekewurm+postgres@gmail.com> Co-authored-by: Peter Geoghegan <pg@bowt.ie> Co-authored-by: Michail Nikolaev <michail.nikolaev@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/CANtu0oi0rkR%2BFsgyLXnGZ-uW2950-urApAWLhy-%2BV1WJD%3D_ZXA%40mail.gmail.com
1 parent 7e0272c commit b798aa8

7 files changed

+505
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s2_mod s1_begin s1_prepare_sorted_asc s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
4+
step s2_mod:
5+
DELETE FROM ios_needs_cleanup_lock WHERE a BETWEEN 2 AND 9;
6+
7+
step s1_begin: BEGIN;
8+
step s1_prepare_sorted_asc:
9+
DECLARE foo NO SCROLL CURSOR FOR SELECT a as x FROM ios_needs_cleanup_lock ORDER BY a ASC;
10+
11+
step s1_fetch_1:
12+
FETCH FROM foo;
13+
14+
x
15+
-
16+
1
17+
(1 row)
18+
19+
step s2_vacuum:
20+
VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
21+
22+
step s1_fetch_all:
23+
FETCH ALL FROM foo;
24+
25+
x
26+
--
27+
10
28+
(1 row)
29+
30+
step s1_commit: COMMIT;
31+
32+
starting permutation: s2_mod s1_begin s1_prepare_sorted_desc s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
33+
step s2_mod:
34+
DELETE FROM ios_needs_cleanup_lock WHERE a BETWEEN 2 AND 9;
35+
36+
step s1_begin: BEGIN;
37+
step s1_prepare_sorted_desc:
38+
DECLARE foo NO SCROLL CURSOR FOR SELECT a as x FROM ios_needs_cleanup_lock ORDER BY a DESC;
39+
40+
step s1_fetch_1:
41+
FETCH FROM foo;
42+
43+
x
44+
--
45+
10
46+
(1 row)
47+
48+
step s2_vacuum:
49+
VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
50+
51+
step s1_fetch_all:
52+
FETCH ALL FROM foo;
53+
54+
x
55+
-
56+
1
57+
(1 row)
58+
59+
step s1_commit: COMMIT;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s2_mod s1_begin s1_prepare_sorted s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
4+
step s2_mod:
5+
DELETE FROM ios_needs_cleanup_lock WHERE a != point '(1,1)';
6+
7+
step s1_begin: BEGIN;
8+
step s1_prepare_sorted:
9+
DECLARE foo NO SCROLL CURSOR FOR SELECT a <-> point '(0,0)' as x FROM ios_needs_cleanup_lock ORDER BY a <-> point '(0,0)';
10+
11+
step s1_fetch_1:
12+
FETCH FROM foo;
13+
14+
x
15+
------------------
16+
1.4142135623730951
17+
(1 row)
18+
19+
step s2_vacuum: VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
20+
step s1_fetch_all:
21+
FETCH ALL FROM foo;
22+
23+
x
24+
-
25+
(0 rows)
26+
27+
step s1_commit: COMMIT;
28+
29+
starting permutation: s2_mod s1_begin s1_prepare_unsorted s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
30+
step s2_mod:
31+
DELETE FROM ios_needs_cleanup_lock WHERE a != point '(1,1)';
32+
33+
step s1_begin: BEGIN;
34+
step s1_prepare_unsorted:
35+
DECLARE foo NO SCROLL CURSOR FOR SELECT a FROM ios_needs_cleanup_lock WHERE box '((-100,-100),(100,100))' @> a;
36+
37+
step s1_fetch_1:
38+
FETCH FROM foo;
39+
40+
a
41+
-----
42+
(1,1)
43+
(1 row)
44+
45+
step s2_vacuum: VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
46+
step s1_fetch_all:
47+
FETCH ALL FROM foo;
48+
49+
a
50+
-
51+
(0 rows)
52+
53+
step s1_commit: COMMIT;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s2_mod s1_begin s1_prepare_sorted s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
4+
step s2_mod:
5+
DELETE FROM ios_needs_cleanup_lock WHERE a != point '(1,1)';
6+
7+
step s1_begin: BEGIN;
8+
step s1_prepare_sorted:
9+
DECLARE foo NO SCROLL CURSOR FOR SELECT a <-> point '(0,0)' as x FROM ios_needs_cleanup_lock ORDER BY a <-> point '(0,0)';
10+
11+
step s1_fetch_1:
12+
FETCH FROM foo;
13+
14+
x
15+
------------------
16+
1.4142135623730951
17+
(1 row)
18+
19+
step s2_vacuum: VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
20+
step s1_fetch_all:
21+
FETCH ALL FROM foo;
22+
23+
x
24+
-
25+
(0 rows)
26+
27+
step s1_commit: COMMIT;
28+
29+
starting permutation: s2_mod s1_begin s1_prepare_unsorted s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
30+
step s2_mod:
31+
DELETE FROM ios_needs_cleanup_lock WHERE a != point '(1,1)';
32+
33+
step s1_begin: BEGIN;
34+
step s1_prepare_unsorted:
35+
DECLARE foo NO SCROLL CURSOR FOR SELECT a FROM ios_needs_cleanup_lock WHERE box '((-100,-100),(100,100))' @> a;
36+
37+
step s1_fetch_1:
38+
FETCH FROM foo;
39+
40+
a
41+
-----
42+
(1,1)
43+
(1 row)
44+
45+
step s2_vacuum: VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
46+
step s1_fetch_all:
47+
FETCH ALL FROM foo;
48+
49+
a
50+
-
51+
(0 rows)
52+
53+
step s1_commit: COMMIT;

src/test/isolation/isolation_schedule

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ test: two-ids
1818
test: multiple-row-versions
1919
test: index-only-scan
2020
test: index-only-bitmapscan
21+
test: index-only-scan-btree-vacuum
22+
test: index-only-scan-gist-vacuum
23+
test: index-only-scan-spgist-vacuum
2124
test: predicate-lock-hot-tuple
2225
test: update-conflict-out
2326
test: deadlock-simple
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# index-only-scan test showing correct results with btree even with concurrent
2+
# vacuum
3+
4+
setup
5+
{
6+
-- by using a low fillfactor and a wide tuple we can get multiple blocks
7+
-- with just few rows
8+
CREATE TABLE ios_needs_cleanup_lock (a int NOT NULL, pad char(1024) default '')
9+
WITH (AUTOVACUUM_ENABLED = false, FILLFACTOR = 10);
10+
11+
INSERT INTO ios_needs_cleanup_lock SELECT g.i FROM generate_series(1, 10) g(i);
12+
13+
CREATE INDEX ios_btree_a ON ios_needs_cleanup_lock USING btree (a);
14+
}
15+
setup
16+
{
17+
VACUUM (ANALYZE) ios_needs_cleanup_lock;
18+
}
19+
20+
teardown
21+
{
22+
DROP TABLE ios_needs_cleanup_lock;
23+
}
24+
25+
26+
session s1
27+
28+
# Force an index-only scan, where possible:
29+
setup {
30+
SET enable_bitmapscan = false;
31+
SET enable_indexonlyscan = true;
32+
SET enable_indexscan = true;
33+
}
34+
35+
step s1_begin { BEGIN; }
36+
step s1_commit { COMMIT; }
37+
38+
step s1_prepare_sorted_asc {
39+
DECLARE foo NO SCROLL CURSOR FOR SELECT a as x FROM ios_needs_cleanup_lock ORDER BY a ASC;
40+
}
41+
step s1_prepare_sorted_desc {
42+
DECLARE foo NO SCROLL CURSOR FOR SELECT a as x FROM ios_needs_cleanup_lock ORDER BY a DESC;
43+
}
44+
45+
step s1_fetch_1 {
46+
FETCH FROM foo;
47+
}
48+
49+
step s1_fetch_all {
50+
FETCH ALL FROM foo;
51+
}
52+
53+
54+
session s2
55+
56+
# Don't delete row 1, nor 10, so we have a row for the cursor to "rest" on.
57+
step s2_mod
58+
{
59+
DELETE FROM ios_needs_cleanup_lock WHERE a BETWEEN 2 AND 9;
60+
}
61+
62+
# Disable truncation, as otherwise we'll just wait for a timeout while trying
63+
# to acquire the lock
64+
step s2_vacuum
65+
{
66+
VACUUM (TRUNCATE false) ios_needs_cleanup_lock;
67+
}
68+
69+
permutation
70+
# delete nearly all rows, to make issue visible
71+
s2_mod
72+
# create a cursor
73+
s1_begin
74+
s1_prepare_sorted_asc
75+
76+
# fetch one row from the cursor, that ensures the index scan portion is done
77+
# before the vacuum in the next step
78+
s1_fetch_1
79+
80+
# with the bug this vacuum will mark pages as all-visible that the scan in
81+
# the next step then considers all-visible, despite all rows from those
82+
# pages having been removed.
83+
# Because this should block on buffer-level locks, this won't ever be
84+
# considered "blocked" by isolation tester, and so we only have a single
85+
# step we can work with concurrently.
86+
s2_vacuum
87+
88+
# if this returns any rows, we're busted
89+
s1_fetch_all
90+
91+
s1_commit
92+
93+
permutation
94+
# delete nearly all rows, to make issue visible
95+
s2_mod
96+
# create a cursor
97+
s1_begin
98+
s1_prepare_sorted_desc
99+
100+
# fetch one row from the cursor, that ensures the index scan portion is done
101+
# before the vacuum in the next step
102+
s1_fetch_1
103+
104+
# If the index scan doesn't correctly interlock its visibility tests with
105+
# concurrent VACUUM cleanup then VACUUM will mark pages as all-visible that
106+
# the scan in the next steps may then consider all-visible, despite some of
107+
# those rows having been removed.
108+
s2_vacuum
109+
110+
# if this returns any rows, we're busted
111+
s1_fetch_all
112+
113+
s1_commit

0 commit comments

Comments
 (0)