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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy