From 67e407e18c64dff51904232ef98f3f992dd82d03 Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Tue, 9 Jun 2026 12:48:19 +0900 Subject: [PATCH 1/2] Reproducer for the streaming replication --- src/test/recovery/meson.build | 1 + src/test/recovery/t/099_repro.pl | 123 +++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/test/recovery/t/099_repro.pl diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index 9eb8ed11425..bfd06a06124 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -62,6 +62,7 @@ tests += { 't/051_effective_wal_level.pl', 't/052_checkpoint_segment_missing.pl', 't/053_standby_login_event_trigger.pl', + 't/099_repro.pl', ], }, } diff --git a/src/test/recovery/t/099_repro.pl b/src/test/recovery/t/099_repro.pl new file mode 100644 index 00000000000..34a97bb19f3 --- /dev/null +++ b/src/test/recovery/t/099_repro.pl @@ -0,0 +1,123 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Reproducer wrong timeline can be chosen during the promotion for the +# streaming replication. + +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 ($stdout, $stderr, $handle); + +my $node_primary = PostgreSQL::Test::Cluster->new('primary'); +my $node_standby = PostgreSQL::Test::Cluster->new('standby'); +my $default_timeout = $PostgreSQL::Test::Utils::timeout_default; + +# Name for the physical slot on primary +my $primary_slotname = 'primary_physical'; +my $standby_physical_slotname = 'standby_physical'; + +######################## +# Initialize primary node +######################## + +$node_primary->init(allows_streaming => 1, has_archiving => 1); +$node_primary->append_conf( + 'postgresql.conf', q{ +wal_level = 'logical' +max_replication_slots = 4 +max_wal_senders = 4 +autovacuum = off +}); +$node_primary->dump_info; +$node_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. +if (!$node_primary->check_extension('injection_points')) +{ + plan skip_all => 'Extension injection_points not installed'; +} + +$node_primary->safe_psql('postgres', 'CREATE EXTENSION injection_points;'); + +$node_primary->safe_psql('postgres', + qq[SELECT * FROM pg_create_physical_replication_slot('$primary_slotname');] +); + +my $backup_name = 'b1'; +$node_primary->backup($backup_name); + +# Some tests need to wait for VACUUM to be replayed. But vacuum does not flush +# WAL. An insert into flush_wal outside transaction does guarantee a flush. +$node_primary->psql('postgres', q[CREATE TABLE flush_wal();]); + +####################### +# Initialize standby node +####################### + +$node_standby->init_from_backup( + $node_primary, $backup_name, + has_streaming => 1, + has_restoring => 1); +$node_standby->append_conf( + 'postgresql.conf', + qq[primary_slot_name = '$primary_slotname' + max_replication_slots = 5]); +$node_standby->start; +$node_primary->wait_for_replay_catchup($node_standby); + +$node_standby->safe_psql('postgres', + "SELECT pg_create_physical_replication_slot('physical_standby');" +); + +# Attach injection point to pause startup after WAL segment cleanup +# but before RecoveryInProgress() flips to false. +$node_standby->safe_psql('postgres', + "SELECT injection_points_attach('promotion-after-wal-segment-cleanup', 'wait');" +); + +# Promote with no-wait so we can synchronize with the injection point. +$node_standby->safe_psql('postgres', "SELECT pg_promote(false)"); + +# Wait for startup to pause after removing old timeline WAL segments. +$node_standby->wait_for_event('startup', + 'promotion-after-wal-segment-cleanup'); + +my $stream_dir = $node_primary->basedir . '/archive_wal'; +mkdir($stream_dir); + +my $log_offset = -s $node_standby->logfile; + +# Start pg_receivewal +$handle = IPC::Run::start( + [ + 'pg_receivewal', + '--directory' => $stream_dir, + '--dbname' => $node_standby->connstr('postgres'), + '--slot' => 'physical_standby', + '--no-loop', + ], + '>' => \$stdout, + '2>' => \$stderr, + IPC::Run::timeout($default_timeout)); + +# XXX wait a bit to make sure pg_receivewal has started +sleep(1); + +# Check the log content +ok( !$node_standby->log_contains( + "requested WAL segment [0-9A-F]+ has already been removed", + $log_offset), + 'check that required WAL segments are still available'); + +done_testing(); -- 2.52.0