From 92637401c5e77a9dcb0ad4dc6a50963aef1e6909 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Date: Fri, 3 Oct 2025 19:45:25 +0900
Subject: [PATCH] Reproduce the slot invalidation on standby

---
 src/test/recovery/meson.build                 |   3 +-
 .../t/050_invalidate_new_slot_on_standby.pl   | 102 ++++++++++++++++++
 2 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100644 src/test/recovery/t/050_invalidate_new_slot_on_standby.pl

diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build
index 9f01d71dc3b..63439cafc25 100644
--- a/src/test/recovery/meson.build
+++ b/src/test/recovery/meson.build
@@ -55,7 +55,8 @@ tests += {
       't/045_archive_restartpoint.pl',
       't/046_checkpoint_logical_slot.pl',
       't/047_checkpoint_physical_slot.pl',
-      't/048_vacuum_horizon_floor.pl'
+      't/048_vacuum_horizon_floor.pl',
+      't/050_invalidate_new_slot_on_standby.pl'
     ],
   },
 }
diff --git a/src/test/recovery/t/050_invalidate_new_slot_on_standby.pl b/src/test/recovery/t/050_invalidate_new_slot_on_standby.pl
new file mode 100644
index 00000000000..bd72d948f39
--- /dev/null
+++ b/src/test/recovery/t/050_invalidate_new_slot_on_standby.pl
@@ -0,0 +1,102 @@
+# This test checks that the new logical slot maybe invalidated on standby
+#
+# In this test some WAL segments are generated in-between the restart_lsn of
+# the slot is set and instance started to protect it.
+# Here we enure that checkpointer does not recycle segments needed by the slot.
+
+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';
+}
+
+# Setup primary node
+my $primary = PostgreSQL::Test::Cluster->new('primary');
+$primary->init(allows_streaming => 1);
+$primary->append_conf(
+	'postgresql.conf', qq(
+	wal_level = logical
+	log_checkpoints = on
+));
+$primary->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+my $result = $primary->safe_psql('postgres',
+	"SELECT count(*) > 0 FROM pg_available_extensions WHERE name = 'injection_points';"
+);
+if ($result eq 'f')
+{
+	plan skip_all => 'Extension injection_points not installed';
+}
+my $backup_name = 'my_backup';
+$primary->backup($backup_name);
+
+# Create streaming standby linking to primary
+my $standby = PostgreSQL::Test::Cluster->new('standby');
+$standby->init_from_backup($primary, $backup_name, has_streaming => 1);
+$standby->start;
+
+# Dummy table for the upcoming tests.
+$primary->safe_psql('postgres', 'checkpoint');
+$primary->safe_psql('postgres', 'CREATE TABLE prim_tab (a int);');
+
+# Install the injection_points on instances
+$primary->safe_psql('postgres', 'CREATE EXTENSION injection_points');
+$primary->wait_for_replay_catchup($standby);
+
+# Attach an injection points on standby
+$result = $standby->safe_psql('postgres',
+	"select injection_points_attach('delay_slot_reserve_wal', 'wait')");
+
+# Create a logical slot on standby. It would stop while determining the
+# startpoint of logical decoding
+my $createslot = $standby->background_psql('postgres', on_error_stop => 0);
+$createslot->query_until(
+	qr/slot_create/, q(
+   \echo slot_create
+SELECT pg_create_logical_replication_slot('logical_slot', 'test_decoding');
+\q
+));
+
+# Log the standby snapshot. If everthing goes well slot can be created faster
+$primary->log_standby_snapshot($standby, 'logical_slot');
+
+# Ensure the slot creation stops
+note('waiting for injection_point');
+$standby->wait_for_event('client backend', 'delay_slot_reserve_wal');
+note('injection_point is reached');
+
+$primary->advance_wal(3);
+
+# Do checkpoint on primary to replicate the REDO record to standby
+$primary->safe_psql('postgres', 'CHECKPOINT');
+$primary->wait_for_replay_catchup($standby);
+
+# And do restartpoint
+my $logstart = -s $standby->logfile;
+$standby->safe_psql('postgres', 'CHECKPOINT');
+
+# Confirs we go the restart point but the slot invalidation has not happened
+ok($standby->log_contains("restartpoint starting:", $logstart),
+	"restartpoint has started");
+
+ok( !$standby->log_contains(
+		"terminating process .* to release replication slot \"logical_slot\"",
+		$logstart),
+	"restartpoint invalidated the logical slot");
+
+# Verify the slot is valid
+$result = $standby->safe_psql('postgres',
+	"SELECT slot_name, invalidation_reason FROM pg_replication_slots");
+is($result, q(logical_slot|), "slot has not been invalidated");
+
+done_testing();
-- 
2.47.3

