From 6229917d4802a82bb63ac41ec32a7ca357701c67 Mon Sep 17 00:00:00 2001
From: alterego655 <824662526@qq.com>
Date: Tue, 25 Nov 2025 19:20:18 +0800
Subject: [PATCH v1 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

