mirror of https://github.com/postgres/postgres
Before commitpull/175/heada0e0fb1ba5
, 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 commit4b211003ec
. 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
parent
4d93bbd4e0
commit
768a9fd553
@ -0,0 +1,124 @@ |
||||
# Copyright (c) 2024, PostgreSQL Global Development Group |
||||
|
||||
# This test verifies edge case of reading a multixact: |
||||
# when we have multixact that is followed by exactly one another multixact, |
||||
# and another multixact have no offset yet, we must wait until this offset |
||||
# becomes observable. Previously we used to wait for 1ms in a loop in this |
||||
# case, but now we use CV for this. This test is exercising such a sleep. |
||||
|
||||
use strict; |
||||
use warnings FATAL => 'all'; |
||||
|
||||
use PostgreSQL::Test::Cluster; |
||||
use PostgreSQL::Test::Utils; |
||||
|
||||
use Test::More; |
||||
|
||||
if ($ENV{enable_injection_points} ne 'yes') |
||||
{ |
||||
plan skip_all => 'Injection points not supported by this build'; |
||||
} |
||||
|
||||
my ($node, $result); |
||||
|
||||
$node = PostgreSQL::Test::Cluster->new('mike'); |
||||
$node->init; |
||||
$node->append_conf('postgresql.conf', |
||||
"shared_preload_libraries = 'test_slru'"); |
||||
$node->start; |
||||
$node->safe_psql('postgres', q(CREATE EXTENSION injection_points)); |
||||
$node->safe_psql('postgres', q(CREATE EXTENSION test_slru)); |
||||
|
||||
# Test for Multixact generation edge case |
||||
$node->safe_psql('postgres', |
||||
q{select injection_points_attach('test-multixact-read','wait')}); |
||||
$node->safe_psql('postgres', |
||||
q{select injection_points_attach('multixact-get-members-cv-sleep','wait')} |
||||
); |
||||
|
||||
# This session must observe sleep on the condition variable while generating a |
||||
# multixact. To achieve this it first will create a multixact, then pause |
||||
# before reading it. |
||||
my $observer = $node->background_psql('postgres'); |
||||
|
||||
# This query will create a multixact, and hang just before reading it. |
||||
$observer->query_until( |
||||
qr/start/, |
||||
q{ |
||||
\echo start |
||||
SELECT test_read_multixact(test_create_multixact()); |
||||
}); |
||||
$node->wait_for_event('client backend', 'test-multixact-read'); |
||||
|
||||
# This session will create the next Multixact. This is necessary to avoid |
||||
# multixact.c's non-sleeping edge case 1. |
||||
my $creator = $node->background_psql('postgres'); |
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_attach('multixact-create-from-members','wait');} |
||||
); |
||||
|
||||
# We expect this query to hang in the critical section after generating new |
||||
# multixact, but before filling it's offset into SLRU. |
||||
# Running an injection point inside a critical section requires it to be |
||||
# loaded beforehand. |
||||
$creator->query_until( |
||||
qr/start/, q{ |
||||
\echo start |
||||
SELECT injection_points_load('multixact-create-from-members'); |
||||
SELECT test_create_multixact(); |
||||
}); |
||||
|
||||
$node->wait_for_event('client backend', 'multixact-create-from-members'); |
||||
|
||||
# Ensure we have the backends waiting that we expect |
||||
is( $node->safe_psql( |
||||
'postgres', |
||||
q{SELECT string_agg(wait_event, ', ' ORDER BY wait_event) |
||||
FROM pg_stat_activity WHERE wait_event_type = 'InjectionPoint'} |
||||
), |
||||
'multixact-create-from-members, test-multixact-read', |
||||
"matching injection point waits"); |
||||
|
||||
# Now wake observer to get it to read the initial multixact. A subsequent |
||||
# multixact already exists, but that one doesn't have an offset assigned, so |
||||
# this will hit multixact.c's edge case 2. |
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_wakeup('test-multixact-read')}); |
||||
$node->wait_for_event('client backend', 'multixact-get-members-cv-sleep'); |
||||
|
||||
# Ensure we have the backends waiting that we expect |
||||
is( $node->safe_psql( |
||||
'postgres', |
||||
q{SELECT string_agg(wait_event, ', ' ORDER BY wait_event) |
||||
FROM pg_stat_activity WHERE wait_event_type = 'InjectionPoint'} |
||||
), |
||||
'multixact-create-from-members, multixact-get-members-cv-sleep', |
||||
"matching injection point waits"); |
||||
|
||||
# Now we have two backends waiting in multixact-create-from-members and |
||||
# multixact-get-members-cv-sleep. Also we have 3 injections points set to wait. |
||||
# If we wakeup multixact-get-members-cv-sleep it will happen again, so we must |
||||
# detach it first. So let's detach all injection points, then wake up all |
||||
# backends. |
||||
|
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_detach('test-multixact-read')}); |
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_detach('multixact-create-from-members')}); |
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_detach('multixact-get-members-cv-sleep')}); |
||||
|
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_wakeup('multixact-create-from-members')}); |
||||
$node->safe_psql('postgres', |
||||
q{SELECT injection_points_wakeup('multixact-get-members-cv-sleep')}); |
||||
|
||||
# Background psql will now be able to read the result and disconnect. |
||||
$observer->quit; |
||||
$creator->quit; |
||||
|
||||
$node->stop; |
||||
|
||||
# If we reached this point - everything is OK. |
||||
ok(1); |
||||
done_testing(); |
@ -0,0 +1,57 @@ |
||||
/*--------------------------------------------------------------------------
|
||||
* |
||||
* test_multixact.c |
||||
* Support code for multixact testing |
||||
* |
||||
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* IDENTIFICATION |
||||
* src/test/modules/test_slru/test_multixact.c |
||||
* |
||||
* ------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include "postgres.h" |
||||
|
||||
#include "access/multixact.h" |
||||
#include "access/xact.h" |
||||
#include "utils/builtins.h" |
||||
#include "utils/injection_point.h" |
||||
|
||||
PG_FUNCTION_INFO_V1(test_create_multixact); |
||||
PG_FUNCTION_INFO_V1(test_read_multixact); |
||||
|
||||
/*
|
||||
* Produces multixact with 2 current xids |
||||
*/ |
||||
Datum |
||||
test_create_multixact(PG_FUNCTION_ARGS) |
||||
{ |
||||
MultiXactId id; |
||||
|
||||
MultiXactIdSetOldestMember(); |
||||
id = MultiXactIdCreate(GetCurrentTransactionId(), MultiXactStatusUpdate, |
||||
GetCurrentTransactionId(), MultiXactStatusForShare); |
||||
PG_RETURN_TRANSACTIONID(id); |
||||
} |
||||
|
||||
/*
|
||||
* Reads given multixact after running an injection point. Discards local cache |
||||
* to make a real read. Tailored for multixact testing. |
||||
*/ |
||||
Datum |
||||
test_read_multixact(PG_FUNCTION_ARGS) |
||||
{ |
||||
MultiXactId id = PG_GETARG_TRANSACTIONID(0); |
||||
MultiXactMember *members; |
||||
|
||||
INJECTION_POINT("test-multixact-read"); |
||||
/* discard caches */ |
||||
AtEOXact_MultiXact(); |
||||
|
||||
if (GetMultiXactIdMembers(id, &members, false, false) == -1) |
||||
elog(ERROR, "MultiXactId not found"); |
||||
|
||||
PG_RETURN_VOID(); |
||||
} |
Loading…
Reference in new issue