1 # Test race conditions involving:
2 # - s1: VACUUM inplace-updating a pg_class row
3 # - s2: GRANT/REVOKE making pg_class rows dead
4 # - s3: "VACUUM pg_class" making dead rows LP_UNUSED; DDL reusing them
6 # Need GRANT to make a non-HOT update. Otherwise, "VACUUM pg_class" would
7 # leave an LP_REDIRECT that persists. To get non-HOT, make rels so the
8 # pg_class row for vactest.orig50 is on a filled page (assuming BLCKSZ=8192).
9 # Just to save on filesystem syscalls, use relkind=c for every other rel.
12 CREATE EXTENSION injection_points
;
13 CREATE SCHEMA vactest
;
14 CREATE FUNCTION vactest.mkrels
(text
, int
, int
) RETURNS void
15 LANGUAGE plpgsql SET search_path
= vactest AS $$
19 FOR i
in $
2 .. $
3 LOOP
21 EXECUTE FORMAT
('CREATE TYPE ' || tname || ' AS
()'
);
22 RAISE DEBUG '% at %'
, tname
, ctid
23 FROM pg_class WHERE oid
= tname
::regclass
;
28 setup
{ VACUUM FULL pg_class
; -- reduce free space
}
31 SELECT vactest.mkrels
('orig'
, 1, 49);
32 CREATE TABLE vactest.orig50
();
33 SELECT vactest.mkrels
('orig'
, 51, 100);
37 DROP SCHEMA vactest CASCADE
;
38 DROP EXTENSION injection_points
;
41 # Wait during inplace update, in a VACUUM of vactest.orig50.
44 SELECT injection_points_set_local
();
45 SELECT injection_points_attach
('inplace
-before
-pin'
, '
wait'
);
47 step vac1
{ VACUUM vactest.orig50
; -- wait during inplace update
}
48 # One bug scenario leaves two live pg_class tuples for vactest.orig50 and zero
49 # live tuples for one of the "intruder" rels. REINDEX observes the duplicate.
51 REINDEX TABLE pg_class
; -- look
for duplicates
52 SELECT reltuples
= -1 AS reltuples_unknown
53 FROM pg_class WHERE oid
= 'vactest.orig50'
::regclass
;
56 # Transactional updates of the tuple vac1 is waiting to inplace-update.
58 step grant2
{ GRANT SELECT ON TABLE vactest.orig50 TO PUBLIC
; }
59 step revoke2
{ REVOKE SELECT ON TABLE vactest.orig50 FROM PUBLIC
; }
60 step begin2
{ BEGIN
; }
64 # Non-blocking actions.
66 step vac3
{ VACUUM pg_class
; }
67 # Reuse the lp that vac1 is waiting to change. I've observed reuse at the 1st
68 # or 18th CREATE, so create excess.
70 SELECT vactest.mkrels
('intruder'
, 1, 100); -- repopulate LP_UNUSED
71 SELECT injection_points_detach
('inplace
-before
-pin'
);
72 SELECT injection_points_wakeup
('inplace
-before
-pin'
);
76 # target gains a successor at the last moment
78 vac1
(mkrels3
) # reads pg_class tuple T0 for vactest.orig50, xmax invalid
79 grant2
# T0 becomes eligible for pruning, T1 is successor
80 vac3
# T0 becomes LP_UNUSED
81 mkrels3
# vac1 wakes, scans to T1
84 # target already has a successor, which commits
87 grant2
# T0.t_ctid = T1
88 vac1
(mkrels3
) # reads T0 for vactest.orig50
89 c2
# T0 becomes eligible for pruning
90 vac3
# T0 becomes LP_UNUSED
91 mkrels3
# vac1 wakes, scans to T1
94 # target already has a successor, which becomes LP_UNUSED at the last moment
97 grant2
# T0.t_ctid = T1
98 vac1
(mkrels3
) # reads T0 for vactest.orig50
99 r2
# T1 becomes eligible for pruning
100 vac3
# T1 becomes LP_UNUSED
101 mkrels3
# reuse T1; vac1 scans to T0
104 # target already has a successor, which becomes LP_REDIRECT at the last moment
107 grant2
# T0.t_ctid = T1, non-HOT due to filled page
108 vac1
(mkrels3
) # reads T0
110 revoke2
# HOT update to T2
111 grant2
# HOT update to T3
112 vac3
# T1 becomes LP_REDIRECT
113 mkrels3
# reuse T2; vac1 scans to T3
116 # waiting for updater to end
118 vac1
(c2
) # reads pg_class tuple T0 for vactest.orig50, xmax invalid
120 grant2
# T0.t_ctid = T1, non-HOT due to filled page
121 revoke2
# HOT update to T2
122 mkrels3
# vac1 awakes briefly, then waits for s2
126 # Another LP_UNUSED. This time, do change the live tuple. Final live tuple
127 # body is identical to original, at a different TID.
130 grant2
# T0.t_ctid = T1, non-HOT due to filled page
131 vac1
(mkrels3
) # reads T0
132 r2
# T1 becomes eligible for pruning
133 grant2
# T0.t_ctid = T2; T0 becomes eligible for pruning
134 revoke2
# T2.t_ctid = T3; T2 becomes eligible for pruning
135 vac3
# T0, T1 & T2 become LP_UNUSED
136 mkrels3
# reuse T0, T1 & T2; vac1 scans to T3
139 # Another LP_REDIRECT. Compared to the earlier test, omit the last grant2.
140 # Hence, final live tuple body is identical to original, at a different TID.
141 permutation begin2 grant2 vac1
(mkrels3
) c2 revoke2 vac3 mkrels3 read1