public inbox for [email protected]
help / color / mirror / Atom feedFrom: Xuneng Zhou <[email protected]>
To: Alexander Korotkov <[email protected]>
Cc: Thomas Munro <[email protected]>
Cc: Álvaro Herrera <[email protected]>
Cc: Chao Li <[email protected]>
Cc: pgsql-hackers <[email protected]>
Cc: Andres Freund <[email protected]>
Cc: Michael Paquier <[email protected]>
Cc: jian he <[email protected]>
Cc: Tomas Vondra <[email protected]>
Cc: Yura Sokolov <[email protected]>
Subject: Re: Implement waiting for wal lsn replay: reloaded
Date: Tue, 6 Jan 2026 21:12:41 +0800
Message-ID: <CABPTF7UtCZW4EcOaTDnBgMxmdsx9RS_d5Q+LbfroQYLzK2g__A@mail.gmail.com> (raw)
In-Reply-To: <CAPpHfdudx4kzV58T0LNZyj+HdYA8bmsgkB+j5x6d9fxCWpdRMA@mail.gmail.com>
References: <CABPTF7Xs-64GQNjmbimZNhj2YSKbBny+evz6=cp3X2fkJS+vMQ@mail.gmail.com>
<[email protected]>
<CAPpHfduDVNo4VXgnQFZUg9=2yHQJfUUqjokbi3qVxuJiKNfcwg@mail.gmail.com>
<CABPTF7UkwQZGx5ub731Q+=+rU8yx4ruqMdDt__L_dm9_32LsMw@mail.gmail.com>
<CAPpHfds-KiZRuCruc0jHxLSxLqzKcHJGwOFFA0b_RgaJvtUOEQ@mail.gmail.com>
<CABPTF7Vy4A0S0W7=B-fVd9Bo_15XG_FRYfKUF2CB_i=5svwEZQ@mail.gmail.com>
<CA+hUKG+L0OQR8dQtsNBSaA3FNNyQeFabdeRVzurm0b7Xtp592g@mail.gmail.com>
<CABPTF7V0ryh6D3SByYCBGuWreTKrg3h0icV5tiFbwJ5YibeV+g@mail.gmail.com>
<CAPpHfdudx4kzV58T0LNZyj+HdYA8bmsgkB+j5x6d9fxCWpdRMA@mail.gmail.com>
Hi,
On Tue, Jan 6, 2026 at 7:54 PM Alexander Korotkov <[email protected]> wrote:
>
> On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <[email protected]> wrote:
> > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <[email protected]> wrote:
> > > Could this be causing the recent flapping failures on CI/macOS in
> > > recovery/031_recovery_conflict? I didn't have time to dig personally
> > > but f30848cb looks relevant:
> > >
> > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
> > > error running SQL: 'psql:<stdin>:1: ERROR: canceling statement due to
> > > conflict with recovery
> > > DETAIL: User was or might have been using tablespace that must be dropped.'
> > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
> > > --dbname port=25195
> > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
> > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
> > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
> > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
> > > line 2300.
> > >
> > > https://cirrus-ci.com/task/5771274900733952
> > >
> > > The master branch in time-descending order, macOS tasks only:
> > >
> > > task_id | substring | status
> > > ------------------+-----------+-----------
> > > 6460882231754752 | c970bdc0 | FAILED
> > > 5771274900733952 | 6ca8506e | FAILED
> > > 6217757068361728 | 63ed3bc7 | FAILED
> > > 5980650261446656 | ae283736 | FAILED
> > > 6585898394976256 | 5f13999a | COMPLETED
> > > 4527474786172928 | 7f9acc9b | COMPLETED
> > > 4826100842364928 | e8d4e94a | COMPLETED
> > > 4540563027918848 | b9ee5f2d | FAILED
> > > 6358528648019968 | c5af141c | FAILED
> > > 5998005284765696 | e212a0f8 | COMPLETED
> > > 6488580526178304 | b85d5dc0 | FAILED
> > > 5034091344560128 | 7dc95cc3 | ABORTED
> > > 5688692477526016 | bb048e31 | COMPLETED
> > > 5481187977723904 | d351063e | COMPLETED
> > > 5101831568752640 | f30848cb | COMPLETED <-- the change
> > > 6395317408497664 | 3f33b63d | COMPLETED
> > > 6741325208354816 | 877ae5db | COMPLETED
> > > 4594007789010944 | de746e0d | COMPLETED
> > > 6497208998035456 | 461b8cc9 | COMPLETED
> >
> > Thanks for raising this issue. I think it is related to f30848cb after
> > some analysis. I'll prepare a follow-up patch to fix it.
>
> Sorry, I've mistakenly referenced this report from commit [1]. I
> thought it was related, but it appears to be not. [1] is related to
> the report I've got from Ruikai Peng off-list.
>
> Regarding the present failure, could it happen before ExecWaitStmt()
> calls PopActiveSnapshot() and InvalidateCatalogSnapshot()? If so, we
> should do preliminary efforts to release these snapshots.
>
> 1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
>
I agree that moving PopActiveSnapshot() and
InvalidateCatalogSnapshot() to the very beginning of ExecWaitStmt()
appears to be a sensible optimization. However, in this particular
failure scenario, it may not address the issue.
For tablespace conflicts, recovery conflict resolution uses
GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid), which
returns all active backends, regardless of their snapshot state. As a
result, even if all snapshots are released at the start of
ExecWaitStmt(), the session would still be canceled during replay of
DROP TABLESPACE.
Given this, I am considering handling this conflict class explicitly:
if the WAIT FOR statement is terminated and the error indicates a
recovery conflict, we fall back to the existing polling-based
approach.
* Ask everybody to cancel their queries immediately so we can ensure no
* temp files remain and we can remove the tablespace. Nuke the entire
* site from orbit, it's the only way to be sure.
*
* XXX: We could work out the pids of active backends using this
* tablespace by examining the temp filenames in the directory. We would
* then convert the pids into VirtualXIDs before attempting to cancel
* them.
I am also wondering whether this optimization would be helpful.
--
Best,
Xuneng
Attachments:
[application/octet-stream] v1-0001-Fix-wait_for_catchup-failure-when-standby-session.patch (4.4K, ../CABPTF7UtCZW4EcOaTDnBgMxmdsx9RS_d5Q+LbfroQYLzK2g__A@mail.gmail.com/2-v1-0001-Fix-wait_for_catchup-failure-when-standby-session.patch)
download | inline diff:
From a9d8230639118d932883c51cc4b6ecf214840022 Mon Sep 17 00:00:00 2001
From: alterego655 <[email protected]>
Date: Tue, 6 Jan 2026 20:55:43 +0800
Subject: [PATCH v1] Fix wait_for_catchup() failure when standby session is
killed by recovery conflict
Commit f30848cb optimized wait_for_catchup() to use WAIT FOR LSN on the standby instead of polling pg_stat_replication on the primary. However, this introduced a failure mode: the WAIT FOR LSN session can be killed by recovery conflicts on the standby, causing the test helper to die unexpectedly.
This manifests as flapping failures in tests like 031_recovery_conflict, where DROP TABLESPACE on the primary triggers ResolveRecoveryConflictWithTablespace() on the standby. That function kills all backends indiscriminately, including the innocent WAIT FOR LSN session that happens to be connected at that moment.
Fix by wrapping the WAIT FOR LSN call in an eval block and falling back to the original polling approach when the session is killed by a recovery conflict. The fallback is selective:
- If WAIT FOR LSN succeeds with 'success': return immediately
- If WAIT FOR LSN returns non-success (timeout, not_in_recovery): fail immediately with diagnostics
- If the session is killed by a recovery conflict (error contains "conflict with recovery"): fall back to polling on the primary
- For any other error: fail immediately to avoid masking real problems
The polling fallback is immune to standby-side conflicts because it queries pg_stat_replication on the primary, not the standby.
---
src/test/perl/PostgreSQL/Test/Cluster.pm | 53 +++++++++++++++++++-----
1 file changed, 42 insertions(+), 11 deletions(-)
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index a28ea89aa10..08379aeb8fb 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -3401,22 +3401,52 @@ sub wait_for_catchup
my $timeout = $PostgreSQL::Test::Utils::timeout_default;
my $wait_query =
qq[WAIT FOR LSN '${target_lsn}' WITH (MODE '${wait_mode}', timeout '${timeout}s', no_throw);];
- my $output = $standby_node->safe_psql('postgres', $wait_query);
- chomp($output);
- if ($output ne 'success')
+ # Try WAIT FOR LSN. If it succeeds, we're done. If it returns a
+ # non-success status (timeout, not_in_recovery), fail immediately.
+ # If the session is interrupted (e.g., killed by recovery conflict),
+ # fall back to polling on the upstream which is immune to standby-
+ # side conflicts.
+ my $output;
+ local $@;
+ my $wait_succeeded = eval {
+ $output = $standby_node->safe_psql('postgres', $wait_query);
+ chomp($output);
+ 1;
+ };
+
+ if ($wait_succeeded && $output eq 'success')
+ {
+ print "done\n";
+ return;
+ }
+
+ # If WAIT FOR LSN executed but returned non-success (e.g., timeout,
+ # not_in_recovery), fail immediately with diagnostic info. Falling
+ # back to polling would just waste time.
+ if ($wait_succeeded)
{
- # Fetch additional detail for debugging purposes
my $details = $self->safe_psql('postgres',
"SELECT * FROM pg_catalog.pg_stat_replication");
- diag qq(WAIT FOR LSN failed with status:
- ${output});
- diag qq(Last pg_stat_replication contents:
- ${details});
- croak "failed waiting for catchup";
+ diag qq(WAIT FOR LSN returned '$output'
+pg_stat_replication on upstream:
+${details});
+ croak "WAIT FOR LSN '$wait_mode' returned '$output'";
+ }
+
+ # WAIT FOR LSN was interrupted. Only fall back to polling if this
+ # looks like a recovery conflict - the canonical PostgreSQL error
+ # message contains "conflict with recovery". Other errors should
+ # fail immediately rather than being masked by a silent fallback.
+ if ($@ =~ /conflict with recovery/i)
+ {
+ diag qq(WAIT FOR LSN interrupted, falling back to polling:
+$@);
+ }
+ else
+ {
+ croak "WAIT FOR LSN failed: $@";
}
- print "done\n";
- return;
}
}
@@ -3424,6 +3454,7 @@ sub wait_for_catchup
# - 'sent' mode (no corresponding WAIT FOR LSN mode)
# - When standby_name is a string (e.g., subscription name)
# - When the standby is no longer in recovery (was promoted)
+ # - When WAIT FOR LSN was interrupted (e.g., killed by a recovery conflict)
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
view thread (12+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: Implement waiting for wal lsn replay: reloaded
In-Reply-To: <CABPTF7UtCZW4EcOaTDnBgMxmdsx9RS_d5Q+LbfroQYLzK2g__A@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox