On 07/03/2026 13:00, Alexander Lakhin wrote:
Hello Xuneng and Heikki,

04.03.2026 07:33, Xuneng Zhou wrote:
03.03.2026 17:39, Heikki Linnakangas wrote:
On 24/02/2026 10:00, Alexander Lakhin wrote:
The "terminating process ..." message doesn't appear when the test passes
successfully.
Hmm, right, looks like something wrong in signaling the recovery conflict. I 
can't tell if the signal is being sent,
or it's not processed correctly. Looking at the code, I don't see anything 
wrong.

I was unable to reproduce the issue on an x86_64 Linux machine using
the provided script. All test runs completed successfully without any
failures.

I've added debug logging (see attached) and saw the following:
!!!SignalRecoveryConflict[282363]
!!!ProcArrayEndTransaction| pendingRecoveryConflicts = 0
!!!ProcessInterrupts[283863]| MyProc->pendingRecoveryConflicts: 0
!!!ProcessInterrupts[283863]| MyProc->pendingRecoveryConflicts: 0
2026-03-07 12:21:24.544 EET walreceiver[282421] FATAL:  could not receive data from WAL stream: server closed the connection unexpectedly
         This probably means the server terminated abnormally
         before or while processing the request.
2026-03-07 12:21:24.645 EET postmaster[282355] LOG:  received immediate shutdown request 2026-03-07 12:21:24.647 EET postmaster[282355] LOG:  database system is shut down

A-ha! So MyProc->pendingRecoveryConflicts is being cleared by ProcArrayEndTransaction(). If I add a small pg_usleep() to the top of ProcArrayEndTransaction(), I can readily reproduce this.

Thanks for narrowing this down. The attached patch fixes it.

- Heikki
From 9edf5432c5717eb3fe02300a5ac75920c7a441ec Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Mon, 9 Mar 2026 13:13:27 +0200
Subject: [PATCH 1/1] Don't clear pendingRecoveryConflicts at end of
 transaction

Commit 17f51ea818 introduced a new pendingRecoveryConflicts field in
PGPROC to replace the various ProcSignals. The new field was cleared
in ProcArrayEndTransaction(), which makes sense for conflicts with
e.g. locks or buffer pins which are gone at end of transaction. But it
is not appropriate for conflicts on a database, or a logical slot.

Because of this, the 035_standby_logical_decoding.pl test was
occasionally getting stuck in the buildfarm. It happens if the startup
process signals recovery conflict with the logical slot just when the
walsender process using the slot calls ProcArrayEndTransaction().

To fix, don't clear pendingRecoveryConflicts in
ProcArrayEndTransaction(). We could still clear certain conflict
flags, like conflicts on locks, but we didn't try to do that before
commit 17f51ea818 either.

In the passing, fix a misspelled comment, and make
InitAuxiliaryProcess() to also clear pendingRecoveryConflicts. I don't
think aux processes can have recovery conflicts, but it seems best to
initialize the field and keep InitAuxiliaryProcess() as close to
InitProcess() as possible.

Analyzed-by: Alexander Lakhin <[email protected]>
Discussion: https://www.postgresql.org/message-id/[email protected]
---
 src/backend/storage/ipc/procarray.c | 7 +------
 src/backend/storage/lmgr/proc.c     | 1 +
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 406b8253f8b..0f913897acc 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -710,8 +710,6 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		/* be sure this is cleared in abort */
 		proc->delayChkptFlags = 0;
 
-		pg_atomic_write_u32(&proc->pendingRecoveryConflicts, 0);
-
 		/* must be cleared with xid/xmin: */
 		/* avoid unnecessarily dirtying shared cachelines */
 		if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
@@ -752,8 +750,6 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
 	/* be sure this is cleared in abort */
 	proc->delayChkptFlags = 0;
 
-	pg_atomic_write_u32(&proc->pendingRecoveryConflicts, 0);
-
 	/* must be cleared with xid/xmin: */
 	/* avoid unnecessarily dirtying shared cachelines */
 	if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
@@ -935,7 +931,6 @@ ProcArrayClearTransaction(PGPROC *proc)
 
 	proc->vxid.lxid = InvalidLocalTransactionId;
 	proc->xmin = InvalidTransactionId;
-	pg_atomic_write_u32(&proc->pendingRecoveryConflicts, 0);
 
 	Assert(!(proc->statusFlags & PROC_VACUUM_STATE_MASK));
 	Assert(!proc->delayChkptFlags);
@@ -3526,7 +3521,7 @@ SignalRecoveryConflictWithVirtualXID(VirtualTransactionId vxid, RecoveryConflict
 }
 
 /*
- * SignalRecoveryConflictWithDatabase --- signal all backends specified database
+ * SignalRecoveryConflictWithDatabase -- signal backends using specified database
  *
  * Like SignalRecoveryConflict, but signals all backends using the database.
  */
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index daf70d9ce2a..d407725e602 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -689,6 +689,7 @@ InitAuxiliaryProcess(void)
 			Assert(dlist_is_empty(&(MyProc->myProcLocks[i])));
 	}
 #endif
+	pg_atomic_write_u32(&MyProc->pendingRecoveryConflicts, 0);
 
 	/*
 	 * Acquire ownership of the PGPROC's latch, so that we can use WaitLatch
-- 
2.47.3

Reply via email to