From 48f072498a128eb47f616e8c7e2621eb1ff2d831 Mon Sep 17 00:00:00 2001 From: alterego655 <824662526@qq.com> Date: Tue, 25 Nov 2025 19:20:18 +0800 Subject: [PATCH v3 5/5] Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup() Replace polling-based catchup waiting with WAIT FOR LSN command when running on a standby server. This is more efficient than repeatedly querying pg_stat_replication as the WAIT FOR command uses the latch- based wakeup mechanism. The optimization applies when: - The node is in recovery (standby server) - The mode is 'replay', 'write', or 'flush' (not 'sent') For 'sent' mode or when running on a primary, the function falls back to the original polling approach since WAIT FOR LSN is only available during recovery. --- src/test/perl/PostgreSQL/Test/Cluster.pm | 35 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm index 35413f14019..b2a4e2e2253 100644 --- a/src/test/perl/PostgreSQL/Test/Cluster.pm +++ b/src/test/perl/PostgreSQL/Test/Cluster.pm @@ -3328,6 +3328,9 @@ sub wait_for_catchup $mode = defined($mode) ? $mode : 'replay'; my %valid_modes = ('sent' => 1, 'write' => 1, 'flush' => 1, 'replay' => 1); + my $isrecovery = + $self->safe_psql('postgres', "SELECT pg_is_in_recovery()"); + chomp($isrecovery); croak "unknown mode $mode for 'wait_for_catchup', valid modes are " . join(', ', keys(%valid_modes)) unless exists($valid_modes{$mode}); @@ -3340,9 +3343,6 @@ sub wait_for_catchup } if (!defined($target_lsn)) { - my $isrecovery = - $self->safe_psql('postgres', "SELECT pg_is_in_recovery()"); - chomp($isrecovery); if ($isrecovery eq 't') { $target_lsn = $self->lsn('replay'); @@ -3360,6 +3360,35 @@ sub wait_for_catchup . $self->name . "\n"; # Before release 12 walreceiver just set the application name to # "walreceiver" + + # Use WAIT FOR LSN when in recovery for supported modes (replay, write, flush) + # This is more efficient than polling pg_stat_replication + if (($mode ne 'sent') && ($isrecovery eq 't')) + { + my $timeout = $PostgreSQL::Test::Utils::timeout_default; + # Map mode names to WAIT FOR LSN MODE values (uppercase) + my $wait_mode = uc($mode); + my $query = + qq[WAIT FOR LSN '${target_lsn}' MODE ${wait_mode} WITH (timeout '${timeout}s', no_throw);]; + my $output = $self->safe_psql('postgres', $query); + chomp($output); + + if ($output ne 'success') + { + # Fetch additional detail for debugging purposes + $query = qq[SELECT * FROM pg_catalog.pg_stat_replication]; + my $details = $self->safe_psql('postgres', $query); + diag qq(WAIT FOR LSN failed with status: +${output}); + diag qq(Last pg_stat_replication contents: +${details}); + croak "failed waiting for catchup"; + } + print "done\n"; + return; + } + + # Polling for 'sent' mode or when not in recovery (WAIT FOR LSN not applicable) my $query = qq[SELECT '$target_lsn' <= ${mode}_lsn AND state = 'streaming' FROM pg_catalog.pg_stat_replication WHERE application_name IN ('$standby_name', 'walreceiver')]; -- 2.51.0