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

Commit 8a695d7

Browse files
author
Amit Kapila
committed
Add a test for commit ac0e331 using the injection point.
This test uses an injection point to bypass the time overhead caused by the idle_replication_slot_timeout GUC, which has a minimum value of one minute. Author: Hayato Kuroda <kuroda.hayato@fujitsu.com> Author: Nisha Moond <nisha.moond412@gmail.com> Reviewed-by: Peter Smith <smithpb2250@gmail.com> Reviewed-by: Vignesh C <vignesh21@gmail.com> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Discussion: https://postgr.es/m/CALj2ACW4aUe-_uFQOjdWCEN-xXoLGhmvRFnL8SNw_TZ5nJe+aw@mail.gmail.com
1 parent 302cf15 commit 8a695d7

File tree

3 files changed

+131
-9
lines changed

3 files changed

+131
-9
lines changed

src/backend/replication/slot.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include "storage/procarray.h"
5757
#include "utils/builtins.h"
5858
#include "utils/guc_hooks.h"
59+
#include "utils/injection_point.h"
5960
#include "utils/varlena.h"
6061

6162
/*
@@ -1669,16 +1670,31 @@ DetermineSlotInvalidationCause(uint32 possible_causes, ReplicationSlot *s,
16691670
{
16701671
Assert(now > 0);
16711672

1672-
/*
1673-
* Check if the slot needs to be invalidated due to
1674-
* idle_replication_slot_timeout GUC.
1675-
*/
1676-
if (CanInvalidateIdleSlot(s) &&
1677-
TimestampDifferenceExceedsSeconds(s->inactive_since, now,
1678-
idle_replication_slot_timeout_mins * SECS_PER_MINUTE))
1673+
if (CanInvalidateIdleSlot(s))
16791674
{
1680-
*inactive_since = s->inactive_since;
1681-
return RS_INVAL_IDLE_TIMEOUT;
1675+
/*
1676+
* We simulate the invalidation due to idle_timeout as the minimum
1677+
* time idle time is one minute which makes tests take a long
1678+
* time.
1679+
*/
1680+
#ifdef USE_INJECTION_POINTS
1681+
if (IS_INJECTION_POINT_ATTACHED("slot-timeout-inval"))
1682+
{
1683+
*inactive_since = 0; /* since the beginning of time */
1684+
return RS_INVAL_IDLE_TIMEOUT;
1685+
}
1686+
#endif
1687+
1688+
/*
1689+
* Check if the slot needs to be invalidated due to
1690+
* idle_replication_slot_timeout GUC.
1691+
*/
1692+
if (TimestampDifferenceExceedsSeconds(s->inactive_since, now,
1693+
idle_replication_slot_timeout_mins * SECS_PER_MINUTE))
1694+
{
1695+
*inactive_since = s->inactive_since;
1696+
return RS_INVAL_IDLE_TIMEOUT;
1697+
}
16821698
}
16831699
}
16841700

src/test/recovery/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ tests += {
5252
't/041_checkpoint_at_promote.pl',
5353
't/042_low_level_backup.pl',
5454
't/043_no_contrecord_switch.pl',
55+
't/044_invalidate_inactive_slots.pl',
5556
],
5657
},
5758
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright (c) 2025, PostgreSQL Global Development Group
2+
3+
# Test for replication slots invalidation due to idle_timeout
4+
use strict;
5+
use warnings FATAL => 'all';
6+
7+
use PostgreSQL::Test::Utils;
8+
use PostgreSQL::Test::Cluster;
9+
use Test::More;
10+
11+
# This test depends on injection point that forces slot invalidation
12+
# due to idle_timeout.
13+
# https://www.postgresql.org/docs/current/xfunc-c.html#XFUNC-ADDIN-INJECTION-POINTS
14+
if ($ENV{enable_injection_points} ne 'yes')
15+
{
16+
plan skip_all => 'Injection points not supported by this build';
17+
}
18+
19+
# Wait for slot to first become idle and then get invalidated
20+
sub wait_for_slot_invalidation
21+
{
22+
my ($node, $slot_name, $offset) = @_;
23+
my $node_name = $node->name;
24+
25+
# The slot's invalidation should be logged
26+
$node->wait_for_log(
27+
qr/invalidating obsolete replication slot \"$slot_name\"/, $offset);
28+
29+
# Check that the invalidation reason is 'idle_timeout'
30+
$node->poll_query_until(
31+
'postgres', qq[
32+
SELECT COUNT(slot_name) = 1 FROM pg_replication_slots
33+
WHERE slot_name = '$slot_name' AND
34+
invalidation_reason = 'idle_timeout';
35+
])
36+
or die
37+
"Timed out while waiting for invalidation reason of slot $slot_name to be set on node $node_name";
38+
}
39+
40+
# ========================================================================
41+
# Testcase start
42+
#
43+
# Test invalidation of physical replication slot and logical replication slot
44+
# due to idle timeout.
45+
46+
# Initialize the node
47+
my $node = PostgreSQL::Test::Cluster->new('node');
48+
$node->init(allows_streaming => 'logical');
49+
50+
# Avoid unpredictability
51+
$node->append_conf(
52+
'postgresql.conf', qq{
53+
checkpoint_timeout = 1h
54+
idle_replication_slot_timeout = 1min
55+
});
56+
$node->start;
57+
58+
# Check if the 'injection_points' extension is available, as it may be
59+
# possible that this script is run with installcheck, where the module
60+
# would not be installed by default.
61+
if (!$node->check_extension('injection_points'))
62+
{
63+
plan skip_all => 'Extension injection_points not installed';
64+
}
65+
66+
# Create both physical and logical replication slots
67+
$node->safe_psql(
68+
'postgres', qq[
69+
SELECT pg_create_physical_replication_slot(slot_name := 'physical_slot', immediately_reserve := true);
70+
SELECT pg_create_logical_replication_slot('logical_slot', 'test_decoding');
71+
]);
72+
73+
my $log_offset = -s $node->logfile;
74+
75+
# Register an injection point on the node to forcibly cause a slot
76+
# invalidation due to idle_timeout
77+
$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
78+
79+
$node->safe_psql('postgres',
80+
"SELECT injection_points_attach('slot-timeout-inval', 'error');");
81+
82+
# Slot invalidation occurs during a checkpoint, so perform a checkpoint to
83+
# invalidate the slots.
84+
$node->safe_psql('postgres', "CHECKPOINT");
85+
86+
# Wait for slots to become inactive. Since nobody has acquired the slot yet,
87+
# it can only be due to the idle timeout mechanism.
88+
wait_for_slot_invalidation($node, 'physical_slot', $log_offset);
89+
wait_for_slot_invalidation($node, 'logical_slot', $log_offset);
90+
91+
# Check that the invalidated slot cannot be acquired
92+
my ($result, $stdout, $stderr);
93+
($result, $stdout, $stderr) = $node->psql(
94+
'postgres', qq[
95+
SELECT pg_replication_slot_advance('logical_slot', '0/1');
96+
]);
97+
ok( $stderr =~ /can no longer access replication slot "logical_slot"/,
98+
"detected error upon trying to acquire invalidated slot on node")
99+
or die
100+
"could not detect error upon trying to acquire invalidated slot \"logical_slot\" on node";
101+
102+
# Testcase end
103+
# =============================================================================
104+
105+
done_testing();

0 commit comments

Comments
 (0)