Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc35f419

Browse files
committed
Add an injection_points isolation test suite.
Make the isolation harness recognize injection_points wait events as atype of blocked state. Test an extant inplace-update bug.Reviewed by Robert Haas and Michael Paquier.Discussion:https://postgr.es/m/20240512232923.aa.nmisch@google.com
1 parentabfbd13 commitc35f419

File tree

6 files changed

+154
-2
lines changed

6 files changed

+154
-2
lines changed

‎src/backend/access/heap/heapam.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include"storage/procarray.h"
6464
#include"storage/standby.h"
6565
#include"utils/datum.h"
66+
#include"utils/injection_point.h"
6667
#include"utils/inval.h"
6768
#include"utils/relcache.h"
6869
#include"utils/snapmgr.h"
@@ -6080,6 +6081,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
60806081
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
60816082
errmsg("cannot update tuples during a parallel operation")));
60826083

6084+
INJECTION_POINT("inplace-before-pin");
60836085
buffer=ReadBuffer(relation,ItemPointerGetBlockNumber(&(tuple->t_self)));
60846086
LockBuffer(buffer,BUFFER_LOCK_EXCLUSIVE);
60856087
page= (Page)BufferGetPage(buffer);

‎src/backend/utils/adt/waitfuncs.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,23 @@
1414

1515
#include"catalog/pg_type.h"
1616
#include"storage/predicate_internals.h"
17+
#include"storage/proc.h"
18+
#include"storage/procarray.h"
1719
#include"utils/array.h"
1820
#include"utils/builtins.h"
21+
#include"utils/wait_event.h"
22+
23+
#defineUINT32_ACCESS_ONCE(var) ((uint32)(*((volatile uint32 *)&(var))))
1924

2025

2126
/*
2227
* pg_isolation_test_session_is_blocked - support function for isolationtester
2328
*
2429
* Check if specified PID is blocked by any of the PIDs listed in the second
2530
* argument. Currently, this looks for blocking caused by waiting for
26-
* heavyweight locks or safe snapshots. We ignore blockage caused by PIDs
27-
* not directly under the isolationtester's control, eg autovacuum.
31+
* injection points, heavyweight locks, or safe snapshots. We ignore blockage
32+
* caused by PIDs not directly under the isolationtester's control, eg
33+
* autovacuum.
2834
*
2935
* This is an undocumented function intended for use by the isolation tester,
3036
* and may change in future releases as required for testing purposes.
@@ -34,6 +40,8 @@ pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
3440
{
3541
intblocked_pid=PG_GETARG_INT32(0);
3642
ArrayType*interesting_pids_a=PG_GETARG_ARRAYTYPE_P(1);
43+
PGPROC*proc;
44+
constchar*wait_event_type;
3745
ArrayType*blocking_pids_a;
3846
int32*interesting_pids;
3947
int32*blocking_pids;
@@ -43,6 +51,15 @@ pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
4351
inti,
4452
j;
4553

54+
/* Check if blocked_pid is in an injection point. */
55+
proc=BackendPidGetProc(blocked_pid);
56+
if (proc==NULL)
57+
PG_RETURN_BOOL(false);/* session gone: definitely unblocked */
58+
wait_event_type=
59+
pgstat_get_wait_event_type(UINT32_ACCESS_ONCE(proc->wait_event_info));
60+
if (wait_event_type&&strcmp("InjectionPoint",wait_event_type)==0)
61+
PG_RETURN_BOOL(true);
62+
4663
/* Validate the passed-in array */
4764
Assert(ARR_ELEMTYPE(interesting_pids_a)==INT4OID);
4865
if (array_contains_nulls(interesting_pids_a))

‎src/test/modules/injection_points/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ PGFILEDESC = "injection_points - facility for injection points"
99
REGRESS = injection_points
1010
REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
1111

12+
ISOLATION = inplace
13+
1214
# The injection points are cluster-wide, so disable installcheck
1315
NO_INSTALLCHECK = 1
1416

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Parsed test spec with 3 sessions
2+
3+
starting permutation: vac1 grant2 vac3 mkrels3 read1
4+
mkrels
5+
------
6+
7+
(1 row)
8+
9+
injection_points_attach
10+
-----------------------
11+
12+
(1 row)
13+
14+
step vac1: VACUUM vactest.orig50; -- wait during inplace update <waiting ...>
15+
step grant2: GRANT SELECT ON TABLE vactest.orig50 TO PUBLIC;
16+
step vac3: VACUUM pg_class;
17+
step mkrels3:
18+
SELECT vactest.mkrels('intruder', 1, 100); -- repopulate LP_UNUSED
19+
SELECT injection_points_detach('inplace-before-pin');
20+
SELECT injection_points_wakeup('inplace-before-pin');
21+
22+
mkrels
23+
------
24+
25+
(1 row)
26+
27+
injection_points_detach
28+
-----------------------
29+
30+
(1 row)
31+
32+
injection_points_wakeup
33+
-----------------------
34+
35+
(1 row)
36+
37+
step vac1: <... completed>
38+
step read1:
39+
REINDEX TABLE pg_class; -- look for duplicates
40+
SELECT reltuples = -1 AS reltuples_unknown
41+
FROM pg_class WHERE oid = 'vactest.orig50'::regclass;
42+
43+
ERROR: could not create unique index "pg_class_oid_index"

‎src/test/modules/injection_points/meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ tests += {
3737
# The injection points are cluster-wide, so disable installcheck
3838
'runningcheck':false,
3939
},
40+
'isolation': {
41+
'specs': [
42+
'inplace',
43+
],
44+
},
4045
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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
5+
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.
10+
setup
11+
{
12+
CREATEEXTENSIONinjection_points;
13+
CREATESCHEMAvactest;
14+
CREATEFUNCTIONvactest.mkrels(text,int,int)RETURNSvoid
15+
LANGUAGEplpgsqlSETsearch_path=vactestAS $$
16+
DECLARE
17+
tnametext;
18+
BEGIN
19+
FORiin $2 .. $3LOOP
20+
tname:= $1||i;
21+
EXECUTEFORMAT('CREATE TYPE '||tname||' AS ()');
22+
RAISEDEBUG'% at %',tname,ctid
23+
FROMpg_classWHEREoid=tname::regclass;
24+
ENDLOOP;
25+
END
26+
$$;
27+
}
28+
setup{VACUUMFULLpg_class;--reducefreespace }
29+
setup
30+
{
31+
SELECTvactest.mkrels('orig',1,49);
32+
CREATETABLEvactest.orig50 ();
33+
SELECTvactest.mkrels('orig',51,100);
34+
}
35+
36+
# XXX DROP causes an assertion failure; adopt DROP once fixed
37+
teardown
38+
{
39+
--DROPSCHEMAvactestCASCADE;
40+
DO $$BEGINEXECUTE'ALTER SCHEMA vactest RENAME TO schema'||oidFROMpg_namespacewherenspname='vactest';END$$;
41+
DROPEXTENSIONinjection_points;
42+
}
43+
44+
# Wait during inplace update, in a VACUUM of vactest.orig50.
45+
sessions1
46+
setup{
47+
SELECTinjection_points_set_local();
48+
SELECTinjection_points_attach('inplace-before-pin','wait');
49+
}
50+
stepvac1{VACUUMvactest.orig50;--waitduringinplaceupdate }
51+
# One bug scenario leaves two live pg_class tuples for vactest.orig50 and zero
52+
# live tuples for one of the "intruder" rels. REINDEX observes the duplicate.
53+
stepread1{
54+
REINDEXTABLEpg_class;--lookforduplicates
55+
SELECTreltuples=-1ASreltuples_unknown
56+
FROMpg_classWHEREoid='vactest.orig50'::regclass;
57+
}
58+
59+
60+
# Transactional updates of the tuple vac1 is waiting to inplace-update.
61+
sessions2
62+
stepgrant2{GRANTSELECTONTABLEvactest.orig50TOPUBLIC; }
63+
64+
65+
# Non-blocking actions.
66+
sessions3
67+
stepvac3{VACUUMpg_class; }
68+
# Reuse the lp that vac1 is waiting to change. I've observed reuse at the 1st
69+
# or 18th CREATE, so create excess.
70+
stepmkrels3{
71+
SELECTvactest.mkrels('intruder',1,100);--repopulateLP_UNUSED
72+
SELECTinjection_points_detach('inplace-before-pin');
73+
SELECTinjection_points_wakeup('inplace-before-pin');
74+
}
75+
76+
77+
# XXX extant bug
78+
permutation
79+
vac1(mkrels3)# reads pg_class tuple T0 for vactest.orig50, xmax invalid
80+
grant2# T0 becomes eligible for pruning, T1 is successor
81+
vac3# T0 becomes LP_UNUSED
82+
mkrels3# T0 reused; vac1 wakes and overwrites the reused T0
83+
read1

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp