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

Commit eeefd42

Browse files
committed
Add TAP tests for timeouts
This commit adds new tests to verify that transaction_timeout, idle_session_timeout, and idle_in_transaction_session_timeout work as expected. We introduce new injection points in before throwing a timeout FATAL error and check these injection points are reached. Discussion: https://postgr.es/m/CAAhFRxiQsRs2Eq5kCo9nXE3HTugsAAJdSQSmxncivebAxdmBjQ%40mail.gmail.com Author: Andrey Borodin Reviewed-by: Alexander Korotkov
1 parent e85662d commit eeefd42

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

src/backend/tcop/postgres.c

+10
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "tcop/tcopprot.h"
7373
#include "tcop/utility.h"
7474
#include "utils/guc_hooks.h"
75+
#include "utils/injection_point.h"
7576
#include "utils/lsyscache.h"
7677
#include "utils/memutils.h"
7778
#include "utils/ps_status.h"
@@ -3411,9 +3412,12 @@ ProcessInterrupts(void)
34113412
* interrupt.
34123413
*/
34133414
if (IdleInTransactionSessionTimeout > 0)
3415+
{
3416+
INJECTION_POINT("idle-in-transaction-session-timeout");
34143417
ereport(FATAL,
34153418
(errcode(ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT),
34163419
errmsg("terminating connection due to idle-in-transaction timeout")));
3420+
}
34173421
else
34183422
IdleInTransactionSessionTimeoutPending = false;
34193423
}
@@ -3422,9 +3426,12 @@ ProcessInterrupts(void)
34223426
{
34233427
/* As above, ignore the signal if the GUC has been reset to zero. */
34243428
if (TransactionTimeout > 0)
3429+
{
3430+
INJECTION_POINT("transaction-timeout");
34253431
ereport(FATAL,
34263432
(errcode(ERRCODE_TRANSACTION_TIMEOUT),
34273433
errmsg("terminating connection due to transaction timeout")));
3434+
}
34283435
else
34293436
TransactionTimeoutPending = false;
34303437
}
@@ -3433,9 +3440,12 @@ ProcessInterrupts(void)
34333440
{
34343441
/* As above, ignore the signal if the GUC has been reset to zero. */
34353442
if (IdleSessionTimeout > 0)
3443+
{
3444+
INJECTION_POINT("idle-session-timeout");
34363445
ereport(FATAL,
34373446
(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
34383447
errmsg("terminating connection due to idle-session timeout")));
3448+
}
34393449
else
34403450
IdleSessionTimeoutPending = false;
34413451
}

src/test/modules/test_misc/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
TAP_TESTS = 1
44

5+
EXTRA_INSTALL=src/test/modules/injection_points
6+
7+
export enable_injection_points enable_injection_points
8+
59
ifdef USE_PGXS
610
PG_CONFIG = pg_config
711
PGXS := $(shell $(PG_CONFIG) --pgxs)

src/test/modules/test_misc/meson.build

+4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ tests += {
55
'sd': meson.current_source_dir(),
66
'bd': meson.current_build_dir(),
77
'tap': {
8+
'env': {
9+
'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
10+
},
811
'tests': [
912
't/001_constraint_validation.pl',
1013
't/002_tablespace.pl',
1114
't/003_check_guc.pl',
1215
't/004_io_direct.pl',
16+
't/005_timeouts.pl'
1317
],
1418
},
1519
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
2+
# Copyright (c) 2024, PostgreSQL Global Development Group
3+
4+
use strict;
5+
use warnings;
6+
use PostgreSQL::Test::Cluster;
7+
use PostgreSQL::Test::Utils;
8+
use Time::HiRes qw(usleep);
9+
use Test::More;
10+
11+
# Test timeouts that will cause FATAL errors. This test relies on injection
12+
# points to await a timeout occurrence. Relying on sleep proved to be unstable
13+
# on buildfarm. It's difficult to rely on the NOTICE injection point because
14+
# the backend under FATAL error can behave differently.
15+
16+
if ($ENV{enable_injection_points} ne 'yes')
17+
{
18+
plan skip_all => 'Injection points not supported by this build';
19+
}
20+
21+
# Node initialization
22+
my $node = PostgreSQL::Test::Cluster->new('master');
23+
$node->init();
24+
$node->start;
25+
$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
26+
27+
#
28+
# 1. Test of the transaction timeout
29+
#
30+
31+
$node->safe_psql('postgres',
32+
"SELECT injection_points_attach('transaction-timeout', 'wait');");
33+
34+
my $psql_session = $node->background_psql('postgres');
35+
36+
# The following query will generate a stream of SELECT 1 queries. This is done
37+
# so to exercise transaction timeout in the presence of short queries.
38+
$psql_session->query_until(
39+
qr/starting_bg_psql/, q(
40+
\echo starting_bg_psql
41+
SET transaction_timeout to '10ms';
42+
BEGIN;
43+
SELECT 1 \watch 0.001
44+
\q
45+
));
46+
47+
# Wait until the backend is in the timeout injection point. Will get an error
48+
# here if anything goes wrong.
49+
$node->wait_for_event('client backend', 'transaction-timeout');
50+
51+
my $log_offset = -s $node->logfile;
52+
53+
# Remove the injection point.
54+
$node->safe_psql('postgres',
55+
"SELECT injection_points_wakeup('transaction-timeout');");
56+
57+
# Check that the timeout was logged.
58+
$node->wait_for_log('terminating connection due to transaction timeout',
59+
$log_offset);
60+
61+
# If we send \q with $psql_session->quit it can get to pump already closed.
62+
# So \q is in initial script, here we only finish IPC::Run.
63+
$psql_session->{run}->finish;
64+
65+
66+
#
67+
# 2. Test of the sidle in transaction timeout
68+
#
69+
70+
$node->safe_psql('postgres',
71+
"SELECT injection_points_attach('idle-in-transaction-session-timeout', 'wait');"
72+
);
73+
74+
# We begin a transaction and the hand on the line
75+
$psql_session = $node->background_psql('postgres');
76+
$psql_session->query_until(
77+
qr/starting_bg_psql/, q(
78+
\echo starting_bg_psql
79+
SET idle_in_transaction_session_timeout to '10ms';
80+
BEGIN;
81+
));
82+
83+
# Wait until the backend is in the timeout injection point.
84+
$node->wait_for_event('client backend',
85+
'idle-in-transaction-session-timeout');
86+
87+
$log_offset = -s $node->logfile;
88+
89+
# Remove the injection point.
90+
$node->safe_psql('postgres',
91+
"SELECT injection_points_wakeup('idle-in-transaction-session-timeout');");
92+
93+
# Check that the timeout was logged.
94+
$node->wait_for_log(
95+
'terminating connection due to idle-in-transaction timeout', $log_offset);
96+
97+
ok($psql_session->quit);
98+
99+
100+
#
101+
# 3. Test of the idle session timeout
102+
#
103+
$node->safe_psql('postgres',
104+
"SELECT injection_points_attach('idle-session-timeout', 'wait');");
105+
106+
# We just initialize the GUC and wait. No transaction is required.
107+
$psql_session = $node->background_psql('postgres');
108+
$psql_session->query_until(
109+
qr/starting_bg_psql/, q(
110+
\echo starting_bg_psql
111+
SET idle_session_timeout to '10ms';
112+
));
113+
114+
# Wait until the backend is in the timeout injection point.
115+
$node->wait_for_event('client backend', 'idle-session-timeout');
116+
117+
$log_offset = -s $node->logfile;
118+
119+
# Remove the injection point.
120+
$node->safe_psql('postgres',
121+
"SELECT injection_points_wakeup('idle-session-timeout');");
122+
123+
# Check that the timeout was logged.
124+
$node->wait_for_log('terminating connection due to idle-session timeout',
125+
$log_offset);
126+
127+
ok($psql_session->quit);
128+
129+
done_testing();

0 commit comments

Comments
 (0)