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

Commit 768a9fd

Browse files
committed
Add injection-point test for new multixact CV usage
Before commit a0e0fb1, multixact.c contained a case in the multixact-read path where it would loop sleeping 1ms each time until another multixact-create path completed, which was uncovered by any tests. That commit changed the code to rely on a condition variable instead. Add a test now, which relies on injection points and "loading" thereof (because of it being in a critical section), per commit 4b21100. Author: Andrey Borodin <x4mmm@yandex-team.ru> Reviewed-by: Michaël Paquier <michael@paquier.xyz> Discussion: https://postgr.es/m/0925F9A9-4D53-4B27-A87E-3D83A757B0E0@yandex-team.ru
1 parent 4d93bbd commit 768a9fd

File tree

6 files changed

+207
-1
lines changed

6 files changed

+207
-1
lines changed

src/backend/access/transam/multixact.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
#include "storage/proc.h"
8989
#include "storage/procarray.h"
9090
#include "utils/fmgrprotos.h"
91+
#include "utils/injection_point.h"
9192
#include "utils/guc_hooks.h"
9293
#include "utils/memutils.h"
9394

@@ -868,6 +869,8 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
868869
*/
869870
multi = GetNewMultiXactId(nmembers, &offset);
870871

872+
INJECTION_POINT("multixact-create-from-members");
873+
871874
/* Make an XLOG entry describing the new MXID. */
872875
xlrec.mid = multi;
873876
xlrec.moff = offset;
@@ -1480,6 +1483,8 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
14801483
LWLockRelease(lock);
14811484
CHECK_FOR_INTERRUPTS();
14821485

1486+
INJECTION_POINT("multixact-get-members-cv-sleep");
1487+
14831488
ConditionVariableSleep(&MultiXactState->nextoff_cv,
14841489
WAIT_EVENT_MULTIXACT_CREATION);
14851490
slept = true;

src/test/modules/test_slru/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
MODULE_big = test_slru
44
OBJS = \
55
$(WIN32RES) \
6-
test_slru.o
6+
test_slru.o \
7+
test_multixact.o
78
PGFILEDESC = "test_slru - test module for SLRUs"
89

10+
EXTRA_INSTALL=src/test/modules/injection_points
11+
export enable_injection_points enable_injection_points
12+
TAP_TESTS = 1
13+
914
EXTENSION = test_slru
1015
DATA = test_slru--1.0.sql
1116

src/test/modules/test_slru/meson.build

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
test_slru_sources = files(
44
'test_slru.c',
5+
'test_multixact.c',
56
)
67

78
if host_system == 'windows'
@@ -32,4 +33,12 @@ tests += {
3233
'regress_args': ['--temp-config', files('test_slru.conf')],
3334
'runningcheck': false,
3435
},
36+
'tap': {
37+
'env': {
38+
'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
39+
},
40+
'tests': [
41+
't/001_multixact.pl'
42+
],
43+
},
3544
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright (c) 2024, PostgreSQL Global Development Group
2+
3+
# This test verifies edge case of reading a multixact:
4+
# when we have multixact that is followed by exactly one another multixact,
5+
# and another multixact have no offset yet, we must wait until this offset
6+
# becomes observable. Previously we used to wait for 1ms in a loop in this
7+
# case, but now we use CV for this. This test is exercising such a sleep.
8+
9+
use strict;
10+
use warnings FATAL => 'all';
11+
12+
use PostgreSQL::Test::Cluster;
13+
use PostgreSQL::Test::Utils;
14+
15+
use Test::More;
16+
17+
if ($ENV{enable_injection_points} ne 'yes')
18+
{
19+
plan skip_all => 'Injection points not supported by this build';
20+
}
21+
22+
my ($node, $result);
23+
24+
$node = PostgreSQL::Test::Cluster->new('mike');
25+
$node->init;
26+
$node->append_conf('postgresql.conf',
27+
"shared_preload_libraries = 'test_slru'");
28+
$node->start;
29+
$node->safe_psql('postgres', q(CREATE EXTENSION injection_points));
30+
$node->safe_psql('postgres', q(CREATE EXTENSION test_slru));
31+
32+
# Test for Multixact generation edge case
33+
$node->safe_psql('postgres',
34+
q{select injection_points_attach('test-multixact-read','wait')});
35+
$node->safe_psql('postgres',
36+
q{select injection_points_attach('multixact-get-members-cv-sleep','wait')}
37+
);
38+
39+
# This session must observe sleep on the condition variable while generating a
40+
# multixact. To achieve this it first will create a multixact, then pause
41+
# before reading it.
42+
my $observer = $node->background_psql('postgres');
43+
44+
# This query will create a multixact, and hang just before reading it.
45+
$observer->query_until(
46+
qr/start/,
47+
q{
48+
\echo start
49+
SELECT test_read_multixact(test_create_multixact());
50+
});
51+
$node->wait_for_event('client backend', 'test-multixact-read');
52+
53+
# This session will create the next Multixact. This is necessary to avoid
54+
# multixact.c's non-sleeping edge case 1.
55+
my $creator = $node->background_psql('postgres');
56+
$node->safe_psql('postgres',
57+
q{SELECT injection_points_attach('multixact-create-from-members','wait');}
58+
);
59+
60+
# We expect this query to hang in the critical section after generating new
61+
# multixact, but before filling it's offset into SLRU.
62+
# Running an injection point inside a critical section requires it to be
63+
# loaded beforehand.
64+
$creator->query_until(
65+
qr/start/, q{
66+
\echo start
67+
SELECT injection_points_load('multixact-create-from-members');
68+
SELECT test_create_multixact();
69+
});
70+
71+
$node->wait_for_event('client backend', 'multixact-create-from-members');
72+
73+
# Ensure we have the backends waiting that we expect
74+
is( $node->safe_psql(
75+
'postgres',
76+
q{SELECT string_agg(wait_event, ', ' ORDER BY wait_event)
77+
FROM pg_stat_activity WHERE wait_event_type = 'InjectionPoint'}
78+
),
79+
'multixact-create-from-members, test-multixact-read',
80+
"matching injection point waits");
81+
82+
# Now wake observer to get it to read the initial multixact. A subsequent
83+
# multixact already exists, but that one doesn't have an offset assigned, so
84+
# this will hit multixact.c's edge case 2.
85+
$node->safe_psql('postgres',
86+
q{SELECT injection_points_wakeup('test-multixact-read')});
87+
$node->wait_for_event('client backend', 'multixact-get-members-cv-sleep');
88+
89+
# Ensure we have the backends waiting that we expect
90+
is( $node->safe_psql(
91+
'postgres',
92+
q{SELECT string_agg(wait_event, ', ' ORDER BY wait_event)
93+
FROM pg_stat_activity WHERE wait_event_type = 'InjectionPoint'}
94+
),
95+
'multixact-create-from-members, multixact-get-members-cv-sleep',
96+
"matching injection point waits");
97+
98+
# Now we have two backends waiting in multixact-create-from-members and
99+
# multixact-get-members-cv-sleep. Also we have 3 injections points set to wait.
100+
# If we wakeup multixact-get-members-cv-sleep it will happen again, so we must
101+
# detach it first. So let's detach all injection points, then wake up all
102+
# backends.
103+
104+
$node->safe_psql('postgres',
105+
q{SELECT injection_points_detach('test-multixact-read')});
106+
$node->safe_psql('postgres',
107+
q{SELECT injection_points_detach('multixact-create-from-members')});
108+
$node->safe_psql('postgres',
109+
q{SELECT injection_points_detach('multixact-get-members-cv-sleep')});
110+
111+
$node->safe_psql('postgres',
112+
q{SELECT injection_points_wakeup('multixact-create-from-members')});
113+
$node->safe_psql('postgres',
114+
q{SELECT injection_points_wakeup('multixact-get-members-cv-sleep')});
115+
116+
# Background psql will now be able to read the result and disconnect.
117+
$observer->quit;
118+
$creator->quit;
119+
120+
$node->stop;
121+
122+
# If we reached this point - everything is OK.
123+
ok(1);
124+
done_testing();
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*--------------------------------------------------------------------------
2+
*
3+
* test_multixact.c
4+
* Support code for multixact testing
5+
*
6+
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7+
* Portions Copyright (c) 1994, Regents of the University of California
8+
*
9+
* IDENTIFICATION
10+
* src/test/modules/test_slru/test_multixact.c
11+
*
12+
* -------------------------------------------------------------------------
13+
*/
14+
15+
#include "postgres.h"
16+
17+
#include "access/multixact.h"
18+
#include "access/xact.h"
19+
#include "utils/builtins.h"
20+
#include "utils/injection_point.h"
21+
22+
PG_FUNCTION_INFO_V1(test_create_multixact);
23+
PG_FUNCTION_INFO_V1(test_read_multixact);
24+
25+
/*
26+
* Produces multixact with 2 current xids
27+
*/
28+
Datum
29+
test_create_multixact(PG_FUNCTION_ARGS)
30+
{
31+
MultiXactId id;
32+
33+
MultiXactIdSetOldestMember();
34+
id = MultiXactIdCreate(GetCurrentTransactionId(), MultiXactStatusUpdate,
35+
GetCurrentTransactionId(), MultiXactStatusForShare);
36+
PG_RETURN_TRANSACTIONID(id);
37+
}
38+
39+
/*
40+
* Reads given multixact after running an injection point. Discards local cache
41+
* to make a real read. Tailored for multixact testing.
42+
*/
43+
Datum
44+
test_read_multixact(PG_FUNCTION_ARGS)
45+
{
46+
MultiXactId id = PG_GETARG_TRANSACTIONID(0);
47+
MultiXactMember *members;
48+
49+
INJECTION_POINT("test-multixact-read");
50+
/* discard caches */
51+
AtEOXact_MultiXact();
52+
53+
if (GetMultiXactIdMembers(id, &members, false, false) == -1)
54+
elog(ERROR, "MultiXactId not found");
55+
56+
PG_RETURN_VOID();
57+
}

src/test/modules/test_slru/test_slru--1.0.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ CREATE OR REPLACE FUNCTION test_slru_page_truncate(bigint) RETURNS VOID
1919
AS 'MODULE_PATHNAME', 'test_slru_page_truncate' LANGUAGE C;
2020
CREATE OR REPLACE FUNCTION test_slru_delete_all() RETURNS VOID
2121
AS 'MODULE_PATHNAME', 'test_slru_delete_all' LANGUAGE C;
22+
23+
24+
CREATE OR REPLACE FUNCTION test_create_multixact() RETURNS xid
25+
AS 'MODULE_PATHNAME', 'test_create_multixact' LANGUAGE C;
26+
CREATE OR REPLACE FUNCTION test_read_multixact(xid) RETURNS VOID
27+
AS 'MODULE_PATHNAME', 'test_read_multixact'LANGUAGE C;

0 commit comments

Comments
 (0)