($INBOX_DIR/description missing)  
help / color / mirror / Atom feed
[PATCH] Add doc details for pg_receivewal with remote_apply
18+ messages / 9 participants
[nested] [flat]

* [PATCH] Add doc details for pg_receivewal with remote_apply
@ 2019-07-24 12:58  Jehan-Guillaume de Rorthais <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Jehan-Guillaume de Rorthais @ 2019-07-24 12:58 UTC (permalink / raw)

---
 doc/src/sgml/ref/pg_receivewal.sgml | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/pg_receivewal.sgml b/doc/src/sgml/ref/pg_receivewal.sgml
index 177e9211c0..abf55ce713 100644
--- a/doc/src/sgml/ref/pg_receivewal.sgml
+++ b/doc/src/sgml/ref/pg_receivewal.sgml
@@ -62,7 +62,12 @@ PostgreSQL documentation
    <varname>application_name</varname> for
    <application>pg_receivewal</application> that does not match it, or
    change the value of <varname>synchronous_commit</varname> to
-   something other than <literal>remote_apply</literal>.
+   something other than <literal>remote_apply</literal> either cluster wide or
+   just for a dedicated role to <application>pg_receivewal</application>:
+<programlisting>
+CREATE ROLE receivewal WITH LOGIN REPLICATION;
+ALTER ROLE receivewal SET synchronous_commit TO on;
+</programlisting>
   </para>
 
   <para>
-- 
2.20.1


--MP_/W7jqwww14IVqmBj_ToIMyry--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH 1/3] Don't use LogicalRepWorker until ->in_use is verified
@ 2023-08-23 07:25  Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Alvaro Herrera @ 2023-08-23 07:25 UTC (permalink / raw)

---
 src/backend/replication/logical/launcher.c | 4 ++--
 src/include/replication/worker_internal.h  | 8 ++++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 72e44d5a02..ec5156aa41 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -862,7 +862,7 @@ logicalrep_sync_worker_count(Oid subid)
 	{
 		LogicalRepWorker *w = &LogicalRepCtx->workers[i];
 
-		if (w->subid == subid && isTablesyncWorker(w))
+		if (isTablesyncWorker(w) && w->subid == subid)
 			res++;
 	}
 
@@ -889,7 +889,7 @@ logicalrep_pa_worker_count(Oid subid)
 	{
 		LogicalRepWorker *w = &LogicalRepCtx->workers[i];
 
-		if (w->subid == subid && isParallelApplyWorker(w))
+		if (isParallelApplyWorker(w) && w->subid == subid)
 			res++;
 	}
 
diff --git a/src/include/replication/worker_internal.h b/src/include/replication/worker_internal.h
index a428663859..8f4bed0958 100644
--- a/src/include/replication/worker_internal.h
+++ b/src/include/replication/worker_internal.h
@@ -327,8 +327,10 @@ extern void pa_decr_and_wait_stream_block(void);
 extern void pa_xact_finish(ParallelApplyWorkerInfo *winfo,
 						   XLogRecPtr remote_lsn);
 
-#define isParallelApplyWorker(worker) ((worker)->type == WORKERTYPE_PARALLEL_APPLY)
-#define isTablesyncWorker(worker) ((worker)->type == WORKERTYPE_TABLESYNC)
+#define isParallelApplyWorker(worker) ((worker)->in_use && \
+									   (worker)->type == WORKERTYPE_PARALLEL_APPLY)
+#define isTablesyncWorker(worker) ((worker)->in_use && \
+								   (worker)->type == WORKERTYPE_TABLESYNC)
 
 static inline bool
 am_tablesync_worker(void)
@@ -339,12 +341,14 @@ am_tablesync_worker(void)
 static inline bool
 am_leader_apply_worker(void)
 {
+	Assert(MyLogicalRepWorker->in_use);
 	return (MyLogicalRepWorker->type == WORKERTYPE_APPLY);
 }
 
 static inline bool
 am_parallel_apply_worker(void)
 {
+	Assert(MyLogicalRepWorker->in_use);
 	return isParallelApplyWorker(MyLogicalRepWorker);
 }
 
-- 
2.30.2


--trxvcc54u5zdf7ul
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="0002-don-t-clean-up-unnecessarily.patch"



^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH 1/3] Don't use LogicalRepWorker until ->in_use is verified
@ 2023-08-23 07:25  Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Alvaro Herrera @ 2023-08-23 07:25 UTC (permalink / raw)

---
 src/backend/replication/logical/launcher.c | 4 ++--
 src/include/replication/worker_internal.h  | 8 ++++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 72e44d5a02..ec5156aa41 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -862,7 +862,7 @@ logicalrep_sync_worker_count(Oid subid)
 	{
 		LogicalRepWorker *w = &LogicalRepCtx->workers[i];
 
-		if (w->subid == subid && isTablesyncWorker(w))
+		if (isTablesyncWorker(w) && w->subid == subid)
 			res++;
 	}
 
@@ -889,7 +889,7 @@ logicalrep_pa_worker_count(Oid subid)
 	{
 		LogicalRepWorker *w = &LogicalRepCtx->workers[i];
 
-		if (w->subid == subid && isParallelApplyWorker(w))
+		if (isParallelApplyWorker(w) && w->subid == subid)
 			res++;
 	}
 
diff --git a/src/include/replication/worker_internal.h b/src/include/replication/worker_internal.h
index a428663859..8f4bed0958 100644
--- a/src/include/replication/worker_internal.h
+++ b/src/include/replication/worker_internal.h
@@ -327,8 +327,10 @@ extern void pa_decr_and_wait_stream_block(void);
 extern void pa_xact_finish(ParallelApplyWorkerInfo *winfo,
 						   XLogRecPtr remote_lsn);
 
-#define isParallelApplyWorker(worker) ((worker)->type == WORKERTYPE_PARALLEL_APPLY)
-#define isTablesyncWorker(worker) ((worker)->type == WORKERTYPE_TABLESYNC)
+#define isParallelApplyWorker(worker) ((worker)->in_use && \
+									   (worker)->type == WORKERTYPE_PARALLEL_APPLY)
+#define isTablesyncWorker(worker) ((worker)->in_use && \
+								   (worker)->type == WORKERTYPE_TABLESYNC)
 
 static inline bool
 am_tablesync_worker(void)
@@ -339,12 +341,14 @@ am_tablesync_worker(void)
 static inline bool
 am_leader_apply_worker(void)
 {
+	Assert(MyLogicalRepWorker->in_use);
 	return (MyLogicalRepWorker->type == WORKERTYPE_APPLY);
 }
 
 static inline bool
 am_parallel_apply_worker(void)
 {
+	Assert(MyLogicalRepWorker->in_use);
 	return isParallelApplyWorker(MyLogicalRepWorker);
 }
 
-- 
2.30.2


--trxvcc54u5zdf7ul
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="0002-don-t-clean-up-unnecessarily.patch"



^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-01-20 17:03  Vitaly Davydov <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Vitaly Davydov @ 2026-01-20 17:03 UTC (permalink / raw)
  To: Fujii Masao <[email protected]>; Andrey Silitskiy <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>

Dear Hackers,

I think, I reproduced test fails. The test fails because walsender is in
waiting state in WalSndDoneImmediate -> ereport with the following stack (see
below). It seems, it tries to send the message to the replica and flush it, but
the replica is hung.

#0  0x00007a4b37f2a037 in epoll_wait
#1  0x000056855317a2e8 in WaitEventSetWaitBlock
#2  WaitEventSetWait
#3  0x0000568552feea8e in secure_write
#4  0x0000568552ff5666 in internal_flush_buffer
#5  0x0000568552ff5966 in internal_flush
#6  socket_flush ()
#7  socket_flush ()
#8  0x00005685532ff1b3 in send_message_to_frontend (edata=<optimized out>)
#9  EmitErrorReport ()
#10 0x00005685532ff6dd in errfinish
#11 0x000056855312cc9c in WalSndDoneImmediate () at walsender.c:3625

I would propose to remove the ereport call from WalSndDoneImmediate.

With best regards,
Vitaly

On 1/19/26 15:41, Fujii Masao wrote:
> On Sun, Jan 18, 2026 at 1:20 AM Andrey Silitskiy
> <[email protected]> wrote:
>>
>> On Jan 9, 2026 at 10:04 AM Fujii Masao
>> <masao(dot)fujii(at)gmail(dot)com> wrote:
>>> Why do we need to send a "done" message to the receiver here?
>>> Since delivery isn't guaranteed in immediate mode, it seems of limited
>>> value.
>>
>> It seems to me that it is better to send a message in cases where it is
>> possible, so as not to raise errors on the subscriber during a clean shutdown.
>> And when this is not possible, exit the process without waiting.
>>
>>> For the immediate mode, would it make sense to log that the walsender is
>>> terminating in immediate mode and that WAL replication may be incomplete,
>>> so users can more easily understand what happened?
>>
>> Added to the latest patch.
> 
> Thanks for updating the patch!
> 
> cfbot is reporting a test failure. Could you please look into it and
> fix the issue?
> https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F6234
> 
> Regards,
> 







^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-01-21 15:11  Andrey Silitskiy <[email protected]>
  parent: Vitaly Davydov <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Andrey Silitskiy @ 2026-01-21 15:11 UTC (permalink / raw)
  To: Vitaly Davydov <[email protected]>; Fujii Masao <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>

Dear Vitaliy,
Thanks for reproducing the error.

The problem occurred during a test case with a full output buffer after
adding ereport about the termination of walsender process in immediate mode.
ereport sends message by default if replica is interested in this logging
level(WARNING level fit this description). And since the output buffer was
already full, the message could not be sent and the process could not exit.

Changed the logging level to LOG_SERVER_ONLY to exclude sending a message
to the replica during immediate shutdown.

In my environment, this error was not reproduced due to the fact that
buffers did not have time to fill up because pg_ctl stop was called
immediately after data was inserted and the buffers did not have time to
fill up and ereport did not hang.

I added a wait for sent_lsn from pg_stat_replication to stop advancing
in the test before server shutdown to give walsender time to fill output
buffers.

Regards,
Andrey Silitskiy

Attachments:

  [text/x-patch] v4-0001-Introduce-a-new-GUC-wal_sender_shutdown_mode.patch (14.1K, ../../[email protected]/2-v4-0001-Introduce-a-new-GUC-wal_sender_shutdown_mode.patch)
  download | inline diff:
From a66a0bbf4a49a12fc3337b7ad15c7efd05369a58 Mon Sep 17 00:00:00 2001
From: "a.silitskiy" <[email protected]>
Date: Fri, 28 Nov 2025 14:40:10 +0700
Subject: [PATCH v4] Introduce a new GUC 'wal_sender_shutdown_mode'.

Previously, at shutdown, walsender processes were always waiting to send all
pending data and ensure that all data is flushed in remote node. But in some cases
an unexpected wait may be unacceptable. For example, in logical replication,
apply_workers may hang on locks for some time, excluding the possibility of
sender's shutdown.

New guc allows to change shutdown mode of walsenders without changing
default behavior.

The shutdown modes are:

1) 'wait_flush' (the default). In this mode, the walsender will wait for all
WALs to be flushed on the receiver side, before exiting the process.

2) 'immediate'. In this mode, the walsender will exit without confirming the
remote flush. This may break the consistency between sender and receiver.
This mode might be useful for a system that has a high-latency network (to
reduce the amount of time for shutdown), or to allow the shutdown of
publisher even when when the subscriber's apply_worker is waiting for any
locks to be released.

Author: Andrey Silitskiy
Co-authored by: Hayato Kuroda
Discussion: https://postgr.es/m/TYAPR01MB586668E50FC2447AD7F92491F5E89%40TYAPR01MB5866.jpnprd01.prod.outlook.com
---
 doc/src/sgml/config.sgml                      |  33 +++++
 src/backend/replication/walsender.c           |  46 +++++++
 src/backend/utils/misc/guc_parameters.dat     |   7 +
 src/backend/utils/misc/guc_tables.c           |   6 +
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/replication/walsender.h           |   7 +
 src/test/subscription/meson.build             |   1 +
 .../t/037_walsnd_immediate_shutdown.pl        | 121 ++++++++++++++++++
 8 files changed, 223 insertions(+)
 create mode 100644 src/test/subscription/t/037_walsnd_immediate_shutdown.pl

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 0fad34da6eb..79218965cf7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4722,6 +4722,39 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-wal_sender_shutdown_mode" xreflabel="wal_sender_shutdown_mode">
+      <term><varname>wal_sender_shutdown_mode</varname> (<type>enum</type>)
+      <indexterm>
+       <primary><varname>wal_sender_shutdown_mode</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the mode in which walsender process will terminate
+        after receival of shutdown request. Valid values are <literal>wait_flush</literal>
+        and <literal>immediate</literal>. Default value is <literal>wait_flush</literal>.
+        Can be set for each walsender.
+       </para>
+       <para>
+        In <literal>wait_flush</literal> mode, the walsender will wait for all
+        WALs to be flushed on the receiver side, before exiting the process. May
+        lead to unexpected lag of server shutdown.
+       </para>
+       <para>
+        In <literal>immediate</literal> mode, the walsender will exit without waiting
+        for data replication to the receiver. This may break data consistency between
+        sender and receiver after shutdown, which can be especially important in
+        case of physical replication and switch-over.
+       </para>
+       <para>
+        This mode might be useful for a system that has a high-latency network (to
+        reduce the amount of time for shutdown), or to allow the shutdown of
+        logical replication walsender even when the subscriber's apply_worker
+        is waiting for any locks to be released.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-synchronized-standby-slots" xreflabel="synchronized_standby_slots">
       <term><varname>synchronized_standby_slots</varname> (<type>string</type>)
       <indexterm>
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 1ab09655a70..f5953ce22d0 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -130,6 +130,9 @@ int			max_wal_senders = 10;	/* the maximum number of concurrent
 									 * walsenders */
 int			wal_sender_timeout = 60 * 1000; /* maximum time to send one WAL
 											 * data message */
+
+int			wal_sender_shutdown_mode = WALSND_SHUTDOWN_MODE_WAIT_FLUSH;
+
 bool		log_replication_commands = false;
 
 /*
@@ -262,6 +265,7 @@ static void WalSndKill(int code, Datum arg);
 pg_noreturn static void WalSndShutdown(void);
 static void XLogSendPhysical(void);
 static void XLogSendLogical(void);
+pg_noreturn static void WalSndDoneImmediate(void);
 static void WalSndDone(WalSndSendDataCallback send_data);
 static void IdentifySystem(void);
 static void UploadManifest(void);
@@ -1657,6 +1661,11 @@ ProcessPendingWrites(void)
 		/* Try to flush pending output to the client */
 		if (pq_flush_if_writable() != 0)
 			WalSndShutdown();
+
+		/* If we got shut down request in immediate shutdown mode, exit the process */
+		if ((got_STOPPING || got_SIGUSR2) &&
+			 wal_sender_shutdown_mode == WALSND_SHUTDOWN_MODE_IMMEDIATE)
+			WalSndDoneImmediate();
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -2934,6 +2943,14 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		if (pq_flush_if_writable() != 0)
 			WalSndShutdown();
 
+		/*
+		 * When immediate shutdown of walsender is requested, we do not
+		 * wait for successfull sending of all data.
+		 */
+		if ((got_STOPPING || got_SIGUSR2) &&
+			 wal_sender_shutdown_mode == WALSND_SHUTDOWN_MODE_IMMEDIATE)
+			WalSndDoneImmediate();
+
 		/* If nothing remains to be sent right now ... */
 		if (WalSndCaughtUp && !pq_is_send_pending())
 		{
@@ -3582,6 +3599,35 @@ XLogSendLogical(void)
 	}
 }
 
+/*
+ * Shutdown walsender in immediate mode.
+ *
+ * NB: This should only be called when immediate shutdown of walsender
+ * was requested and shutdown signal has been received from postmaster.
+ */
+static void
+WalSndDoneImmediate()
+{
+	QueryCompletion qc;
+
+	/* Try to inform receiver that XLOG streaming is done */
+	SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+	EndCommand(&qc, DestRemote, false);
+
+	/*
+	 * Note that the output buffer may be full during immediate shutdown of
+	 * walsender. If pq_flush() is called at that time, the walsender process
+	 * will be stuck. Therefore, call pq_flush_if_writable() instead. Successfull
+	 * receival of done message in immediate shutdown mode is not guaranteed.
+	 */
+	pq_flush_if_writable();
+
+	ereport(LOG_SERVER_ONLY,
+			(errmsg("walsender shutting down in immediate mode: replication may be incomplete")));
+	proc_exit(0);
+	abort();
+}
+
 /*
  * Shutdown if the sender is caught up.
  *
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 7c60b125564..96433fa6015 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -3431,6 +3431,13 @@
   check_hook => 'check_wal_segment_size',
 },
 
+{ name => 'wal_sender_shutdown_mode', type => 'enum', context => 'PGC_USERSET', group => 'REPLICATION_SENDING',
+  short_desc => 'Sets the mode in which walsender will be terminated after shutdown request.',
+  variable => 'wal_sender_shutdown_mode',
+  boot_val => 'WALSND_SHUTDOWN_MODE_WAIT_FLUSH',
+  options => 'wal_sender_shutdown_mode_options',
+},
+
 { name => 'wal_sender_timeout', type => 'int', context => 'PGC_USERSET', group => 'REPLICATION_SENDING',
   short_desc => 'Sets the maximum time to wait for WAL replication.',
   flags => 'GUC_UNIT_MS',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 73ff6ad0a32..f2cbb0c700a 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -335,6 +335,12 @@ static const struct config_enum_entry constraint_exclusion_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry wal_sender_shutdown_mode_options[] = {
+	{"wait_flush", WALSND_SHUTDOWN_MODE_WAIT_FLUSH, false},
+	{"immediate", WALSND_SHUTDOWN_MODE_IMMEDIATE, false},
+	{NULL, 0, false}
+};
+
 /*
  * Although only "on", "off", "remote_apply", "remote_write", and "local" are
  * documented, we accept all the likely variants of "on" and "off".
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index dc9e2255f8a..d3c9c1d3d54 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -346,6 +346,8 @@
 #wal_sender_timeout = 60s       # in milliseconds; 0 disables
 #track_commit_timestamp = off   # collect timestamp of transaction commit
                                 # (change requires restart)
+#wal_sender_shutdown_mode = wait_flush  # walsender termination mode after
+                                # receival of shutdown request
 
 # - Primary Server -
 
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
index a4df3b8e0ae..3f962ea2792 100644
--- a/src/include/replication/walsender.h
+++ b/src/include/replication/walsender.h
@@ -24,6 +24,12 @@ typedef enum
 	CRS_USE_SNAPSHOT,
 } CRSSnapshotAction;
 
+typedef enum
+{
+	WALSND_SHUTDOWN_MODE_WAIT_FLUSH = 0,
+	WALSND_SHUTDOWN_MODE_IMMEDIATE
+} WalSndShutdownMode;
+
 /* global state */
 extern PGDLLIMPORT bool am_walsender;
 extern PGDLLIMPORT bool am_cascading_walsender;
@@ -33,6 +39,7 @@ extern PGDLLIMPORT bool wake_wal_senders;
 /* user-settable parameters */
 extern PGDLLIMPORT int max_wal_senders;
 extern PGDLLIMPORT int wal_sender_timeout;
+extern PGDLLIMPORT int wal_sender_shutdown_mode;
 extern PGDLLIMPORT bool log_replication_commands;
 
 extern void InitWalSender(void);
diff --git a/src/test/subscription/meson.build b/src/test/subscription/meson.build
index a4c7dbaff59..3b9d27fba0b 100644
--- a/src/test/subscription/meson.build
+++ b/src/test/subscription/meson.build
@@ -46,6 +46,7 @@ tests += {
       't/034_temporal.pl',
       't/035_conflicts.pl',
       't/036_sequences.pl',
+      't/037_walsnd_immediate_shutdown.pl',
       't/100_bugs.pl',
     ],
   },
diff --git a/src/test/subscription/t/037_walsnd_immediate_shutdown.pl b/src/test/subscription/t/037_walsnd_immediate_shutdown.pl
new file mode 100644
index 00000000000..c991b274cd0
--- /dev/null
+++ b/src/test/subscription/t/037_walsnd_immediate_shutdown.pl
@@ -0,0 +1,121 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Checks that publisher is able to shut down without
+# waiting for sending of all pending data to subscriber
+# with wal_sender_shutdown_mode = immediate
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+# create publisher
+my $publisher = PostgreSQL::Test::Cluster->new('publisher');
+$publisher->init(allows_streaming => 'logical');
+# set wal_sender_shutdown_mode GUC parameter to immediate
+$publisher->append_conf('postgresql.conf',
+	"wal_sender_timeout = 0
+	 wal_sender_shutdown_mode = 'immediate'");
+$publisher->start();
+
+# create subscriber
+my $subscriber = PostgreSQL::Test::Cluster->new('subscriber');
+$subscriber->init();
+$subscriber->append_conf('postgresql.conf',
+	"wal_receiver_status_interval = 1");
+$subscriber->start();
+
+# create publication for test table
+$publisher->safe_psql('postgres', q{
+	CREATE TABLE pub_test (id int PRIMARY KEY);
+	CREATE PUBLICATION pub_all FOR TABLE pub_test;
+});
+
+# create matching table on subscriber
+$subscriber->safe_psql('postgres', q{
+	CREATE TABLE pub_test (id int PRIMARY KEY);
+});
+
+# form connection string to publisher
+my $pub_connstr = $publisher->connstr;
+
+# create the subscription on subscriber
+$subscriber->safe_psql('postgres', qq{
+	CREATE SUBSCRIPTION sub_all
+	CONNECTION '$pub_connstr'
+	PUBLICATION pub_all;
+});
+
+# wait for initial sync to finish
+$subscriber->wait_for_subscription_sync($publisher, 'sub_all');
+
+# create background psql session
+my $bpgsql = $subscriber->background_psql('postgres', on_error_stop => 0);
+
+# =============================================================================
+# Testcase start: Shutdown of publisher with empty output buffers
+
+# start transaction on subscriber to hold locks
+$bpgsql->query_safe(
+	"BEGIN; INSERT INTO pub_test VALUES (0);"
+);
+
+# run concurrent transaction on publisher and commit
+$publisher->safe_psql('postgres', 'BEGIN; INSERT INTO pub_test VALUES (0); COMMIT;');
+
+# test publisher shutdown
+$publisher->stop('fast');
+pass('successfull fast shutdown of server with empty output buffers');
+
+# Testcase end: Shutdown of publisher with empty output buffers
+# =============================================================================
+
+$bpgsql->query_safe(
+	"ABORT;"
+);
+
+# restart publisher for the next testcase
+$publisher->start();
+
+$publisher->wait_for_catchup('sub_all');
+
+# =============================================================================
+# Testcase start: Shutdown of publisher with full output buffers
+
+# lock table to make apply_worker hang
+$bpgsql->query_safe(
+	"BEGIN; LOCK TABLE pub_test IN EXCLUSIVE MODE;"
+);
+
+my $last_sent_lsn = $publisher->safe_psql('postgres', "select sent_lsn from pg_stat_replication where application_name = 'sub_all';");
+my $cur_sent_lsn;
+
+# generate big amount of wal records for locked table
+$publisher->safe_psql('postgres', 'BEGIN; INSERT INTO pub_test SELECT i from generate_series(1, 25000) s(i); COMMIT;');
+
+# waiting for walsender to fill output buffers
+my $max_attempts = $PostgreSQL::Test::Utils::timeout_default;
+while ($max_attempts-- >= 0)
+{
+	sleep 1;
+
+	$cur_sent_lsn = $publisher->safe_psql('postgres', "select sent_lsn from pg_stat_replication where application_name = 'sub_all';");
+
+	my $diff = $publisher->safe_psql('postgres', qq(
+		SELECT pg_wal_lsn_diff('$cur_sent_lsn', '$last_sent_lsn');
+	));
+
+	last if $diff == 0;
+
+	$last_sent_lsn = $cur_sent_lsn;
+}
+
+# test publisher shutdown
+$publisher->stop('fast');
+pass('successfull fast shutdown of server with full output buffers');
+
+# Testcase end: Shutdown of publisher with full output buffers
+# =============================================================================
+
+done_testing();
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-01-26 14:08  Fujii Masao <[email protected]>
  parent: Andrey Silitskiy <[email protected]>
  0 siblings, 2 replies; 18+ messages in thread

From: Fujii Masao @ 2026-01-26 14:08 UTC (permalink / raw)
  To: Andrey Silitskiy <[email protected]>; +Cc: Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>

On Thu, Jan 22, 2026 at 12:11 AM Andrey Silitskiy
<[email protected]> wrote:
>
> Dear Vitaliy,
> Thanks for reproducing the error.
>
> The problem occurred during a test case with a full output buffer after
> adding ereport about the termination of walsender process in immediate mode.
> ereport sends message by default if replica is interested in this logging
> level(WARNING level fit this description). And since the output buffer was
> already full, the message could not be sent and the process could not exit.
>
> Changed the logging level to LOG_SERVER_ONLY to exclude sending a message
> to the replica during immediate shutdown.

You changed the log level used by ereport() in WalSndDoneImmediate() to
LOG_SERVER_ONLY, but proc_exit() can still emit messages at other levels.
That could result in attempts to send additional messages to the standby.
To avoid this, should whereToSendOutput be reset, as is done
in WalSndShutdown()?


> In my environment, this error was not reproduced due to the fact that
> buffers did not have time to fill up because pg_ctl stop was called
> immediately after data was inserted and the buffers did not have time to
> fill up and ereport did not hang.
>
> I added a wait for sent_lsn from pg_stat_replication to stop advancing
> in the test before server shutdown to give walsender time to fill output
> buffers.

Thanks for addressing the issue and updating the patch!


There seems also an alternative way to implement immediate shutdown of
walsenders: if wal_sender_shutdown_mode is set to immediate, the postmaster
could send SIGTERM to walsenders, just as it does to backends. In that case,
walsenders would exit without waiting for WAL replication, and the shutdown
sequence could proceed normally. With this approach, WalSndDoneImmediate()
would no longer be needed. I'm still unsure which approach is better....

One issue with the SIGTERM-based approach is that walsenders call
ereport(FATAL) on exit and may try to send the error message to the standby.
If the send buffer is full, the walsender could block in ereport(). This would
need to be addressed if we go that route.

BTW, this issue can also occur when terminating a walsender via
pg_terminate_backend() (for maintenance, etc.). If that is considered
problematic on its own, it might be better handled as a separate patch first.


Regarding the GUC name, wal_sender_shutdown seems simple and sufficient to me.
This isn't a blocker, so I'm fine with the current name for now and
revisiting it later if needed.


On the documentation, there's at least one reference to shutdown behavior
in high-availability.sgml. Since this patch changes walsender shutdown behavior,
that section should be updated as well.


+        In <literal>immediate</literal> mode, the walsender will exit
without waiting
+        for data replication to the receiver. This may break data
consistency between
+        sender and receiver after shutdown, which can be especially
important in
+        case of physical replication and switch-over.

It seems clearer to clarify the benefit of wait_flush in relation to
switchovers, rather than focusing only risks. For example:

----------------------
Specifies how a walsender process terminates after receiving a shutdown
request. Valid values are wait_flush and immediate. The default is wait_flush.
This setting can be configured per walsender.

In wait_flush mode, the walsender waits until all WAL data has been flushed on
the receiver before exiting. This helps keep the sender and receiver in sync
after shutdown, which is especially important for physical replication
switchovers. However, it can delay server shutdown.

In immediate mode, the walsender exits without waiting for WAL data to be
replicated to the receiver. This can reduce shutdown time when flushing
WAL data to the receiver would take a long time, for example on high-latency
networks or when the subscriber's apply worker is blocked waiting for locks
in logical replication.
----------------------


There's no strict rule for the ordering of parameter descriptions, but it seems
more intuitive to place wal_sender_shutdown_mode immediately after
wal_sender_timeout, rather than after track_commit_timestamp,
since the former two are more closely related. If we do that,
postgresql.conf.sample should be updated accordingly.


+#wal_sender_shutdown_mode = wait_flush  # walsender termination mode after
+                                # receival of shutdown request

In postgresql.conf.sample, enum GUCs typically list their valid values in
the comment. It would be good to do the same for wal_sender_shutdown_mode.


At the top of walsender.c, there are comments describing walsender behavior,
including shutdown handling. Since this patch changes shutdown behavior,
those comments should be updated.


+typedef enum
+{
+ WALSND_SHUTDOWN_MODE_WAIT_FLUSH = 0,
+ WALSND_SHUTDOWN_MODE_IMMEDIATE
+} WalSndShutdownMode;

WalSndShutdownMode should be added into src/tools/pgindent/typedefs.list
for pgindent.


+ * NB: This should only be called when immediate shutdown of walsender
+ * was requested and shutdown signal has been received from postmaster.

The comment on WalSndDoneImmediate() says it is only called after a shutdown
signal from the postmaster, but it can also be called when the checkpointer
signals walsenders to move to the stopping state (got_STOPPING = true),
which may happen earlier. So this comment seems to need to be updated.
Or, if this comment is true, WalSndDoneImmediate() should not be called under
got_STOPPING=true.


+ proc_exit(0);
+ abort();
+}

abort() call at the end of WalSndDoneImmediate() does not seem necessary.
Please see the discussion [1].

Regards,

[1] https://postgr.es/m/CAHGQGwHPX1yoixq+YB5rF4zL90TMmSEa3FpHURtqW3Jc5+=oSA@mail.gmail.com

Regards,

-- 
Fujii Masao






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-01-28 08:27  Ronan Dunklau <[email protected]>
  parent: Fujii Masao <[email protected]>
  1 sibling, 1 reply; 18+ messages in thread

From: Ronan Dunklau @ 2026-01-28 08:27 UTC (permalink / raw)
  To: Fujii Masao <[email protected]>; +Cc: Andrey Silitskiy <[email protected]>; Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>

On Monday, January 26th, 2026 at 15:08, Fujii Masao <[email protected]> wrote:
 
> Regarding the GUC name, wal_sender_shutdown seems simple and sufficient to me.
> This isn't a blocker, so I'm fine with the current name for now and
> revisiting it later if needed.

Are we considering other approaches to this ? Having the behavior be either "wait indefinitely" or "terminate immediately" is a bit coarse I think: a timeout for the wait (maybe named wal_sender_stop_timeout ?) would allow for the same usage as this patch provides (set it to -1 for indefinite wait, 0 for immediate shutdown, or any positive value to give a chance to the walsender to catch up before we terminate it forcibly).

The problem we have as of now is when the walreceiver is indeed connected and not reaching wal_sender_timeout as it's still processing: a distinct timeout would alleviate that.

Regards,

--
Ronan Dunklau






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-01-28 11:27  Andrey Silitskiy <[email protected]>
  parent: Fujii Masao <[email protected]>
  1 sibling, 1 reply; 18+ messages in thread

From: Andrey Silitskiy @ 2026-01-28 11:27 UTC (permalink / raw)
  To: Fujii Masao <[email protected]>; +Cc: Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>

Thanks for the review!

Updatedthe patchbasedonyourcomments. Regards, Andrey Silitskiy

Attachments:

  [text/x-patch] v5-0001-Introduce-a-new-GUC-wal_sender_shutdown_mode.patch (16.1K, ../../[email protected]/3-v5-0001-Introduce-a-new-GUC-wal_sender_shutdown_mode.patch)
  download | inline diff:
From 948127c0edef957559c5ae6e61b81d75f394831d Mon Sep 17 00:00:00 2001
From: "a.silitskiy" <[email protected]>
Date: Fri, 28 Nov 2025 14:40:10 +0700
Subject: [PATCH v5] Introduce a new GUC 'wal_sender_shutdown_mode'.

Previously, at shutdown, walsender processes were always waiting to send all
pending data and ensure that all data is flushed in remote node. But in some cases
an unexpected wait may be unacceptable. For example, in logical replication,
apply_workers may hang on locks for some time, excluding the possibility of
sender's shutdown.

New guc allows to change shutdown mode of walsenders without changing
default behavior.

The shutdown modes are:

1) 'wait_flush' (the default). In this mode, the walsender will wait for all
WALs to be flushed on the receiver side, before exiting the process.

2) 'immediate'. In this mode, the walsender will exit without confirming the
remote flush. This may break the consistency between sender and receiver.
This mode might be useful for a system that has a high-latency network (to
reduce the amount of time for shutdown), or to allow the shutdown of
publisher even when when the subscriber's apply_worker is waiting for any
locks to be released.

Author: Andrey Silitskiy
Co-authored by: Hayato Kuroda
Discussion: https://postgr.es/m/TYAPR01MB586668E50FC2447AD7F92491F5E89%40TYAPR01MB5866.jpnprd01.prod.outlook.com
---
 doc/src/sgml/config.sgml                      |  30 +++++
 doc/src/sgml/high-availability.sgml           |   9 +-
 src/backend/replication/walsender.c           |  52 ++++++++
 src/backend/utils/misc/guc_parameters.dat     |   7 +
 src/backend/utils/misc/guc_tables.c           |   6 +
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/replication/walsender.h           |   7 +
 src/test/subscription/meson.build             |   1 +
 .../t/037_walsnd_immediate_shutdown.pl        | 121 ++++++++++++++++++
 src/tools/pgindent/typedefs.list              |   1 +
 10 files changed, 232 insertions(+), 4 deletions(-)
 create mode 100644 src/test/subscription/t/037_walsnd_immediate_shutdown.pl

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5560b95ee60..c8c5e736cd5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4722,6 +4722,36 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-wal_sender_shutdown_mode" xreflabel="wal_sender_shutdown_mode">
+      <term><varname>wal_sender_shutdown_mode</varname> (<type>enum</type>)
+      <indexterm>
+       <primary><varname>wal_sender_shutdown_mode</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the mode in which walsender process will terminate
+        after receival of shutdown request. Valid values are <literal>wait_flush</literal>
+        and <literal>immediate</literal>. Default value is <literal>wait_flush</literal>.
+        Can be set for each walsender.
+       </para>
+       <para>
+        In <literal>wait_flush</literal> mode, the walsender will wait for all
+        WAL data to be flushed on the receiver side, before exiting the process.
+        This helps to keep the sender and receiver in sync after shutdown, which is
+        especially important for physical replication switchovers. However,
+        it can delay server shutdown.
+       </para>
+       <para>
+        In <literal>immediate</literal> mode, the walsender will exit without waiting
+        for WAL data replication to the receiver. This can reduce shutdown time when
+        flushing WAL data to the receiver would take a long time, for example on
+        high-latency networks or when the subscriber's apply worker is blocked waiting
+        for locks in logical replication.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-synchronized-standby-slots" xreflabel="synchronized_standby_slots">
       <term><varname>synchronized_standby_slots</varname> (<type>string</type>)
       <indexterm>
diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index c3f269e0364..65e119f4851 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -1190,10 +1190,11 @@ primary_slot_name = 'node_a_slot'
    </para>
 
    <para>
-    Users will stop waiting if a fast shutdown is requested.  However, as
-    when using asynchronous replication, the server will not fully
-    shutdown until all outstanding WAL records are transferred to the currently
-    connected standby servers.
+    Users will stop waiting if a fast shutdown is requested.  However, if
+    <varname>wal_sender_shutdown_mode<varname> is set to <literal>wait_flush<literal>, the
+    server will not fully shutdown until all outstanding WAL records are
+    transferred to the currently connected standby servers. This waiting
+    applies to both asynchronous and synchronous replication.
    </para>
 
    </sect3>
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0e6a3d200c..80cd6086908 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -35,6 +35,8 @@
  * checkpoint finishes, the postmaster sends us SIGUSR2. This instructs
  * walsender to send any outstanding WAL, including the shutdown checkpoint
  * record, wait for it to be replicated to the standby, and then exit.
+ * If wal_sender_shutdown_mode = immediate is set, walsender process will
+ * skip these steps and terminate after receival of shutdown request.
  *
  *
  * Portions Copyright (c) 2010-2026, PostgreSQL Global Development Group
@@ -130,6 +132,9 @@ int			max_wal_senders = 10;	/* the maximum number of concurrent
 									 * walsenders */
 int			wal_sender_timeout = 60 * 1000; /* maximum time to send one WAL
 											 * data message */
+
+int			wal_sender_shutdown_mode = WALSND_SHUTDOWN_MODE_WAIT_FLUSH;
+
 bool		log_replication_commands = false;
 
 /*
@@ -262,6 +267,7 @@ static void WalSndKill(int code, Datum arg);
 pg_noreturn static void WalSndShutdown(void);
 static void XLogSendPhysical(void);
 static void XLogSendLogical(void);
+pg_noreturn static void WalSndDoneImmediate(void);
 static void WalSndDone(WalSndSendDataCallback send_data);
 static void IdentifySystem(void);
 static void UploadManifest(void);
@@ -1656,6 +1662,11 @@ ProcessPendingWrites(void)
 		/* Try to flush pending output to the client */
 		if (pq_flush_if_writable() != 0)
 			WalSndShutdown();
+
+		/* If we got shut down request in immediate shutdown mode, exit the process */
+		if ((got_STOPPING || got_SIGUSR2) &&
+			 wal_sender_shutdown_mode == WALSND_SHUTDOWN_MODE_IMMEDIATE)
+			WalSndDoneImmediate();
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -2933,6 +2944,14 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		if (pq_flush_if_writable() != 0)
 			WalSndShutdown();
 
+		/*
+		 * When immediate shutdown of walsender is requested, we do not
+		 * wait for successfull sending of all data.
+		 */
+		if ((got_STOPPING || got_SIGUSR2) &&
+			 wal_sender_shutdown_mode == WALSND_SHUTDOWN_MODE_IMMEDIATE)
+			WalSndDoneImmediate();
+
 		/* If nothing remains to be sent right now ... */
 		if (WalSndCaughtUp && !pq_is_send_pending())
 		{
@@ -3581,6 +3600,39 @@ XLogSendLogical(void)
 	}
 }
 
+/*
+ * Shutdown walsender in immediate mode.
+ */
+static void
+WalSndDoneImmediate()
+{
+	QueryCompletion qc;
+
+	/* Try to inform receiver that XLOG streaming is done */
+	SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+	EndCommand(&qc, DestRemote, false);
+
+	/*
+	 * Note that the output buffer may be full during immediate shutdown of
+	 * walsender. If pq_flush() is called at that time, the walsender process
+	 * will be stuck. Therefore, call pq_flush_if_writable() instead. Successfull
+	 * receival of done message in immediate shutdown mode is not guaranteed.
+	 */
+	pq_flush_if_writable();
+
+	/*
+	 * Prevent ereport from attempting to send any more messages to the standby.
+	 * Otherwise it can cause process stuck if output buffers are full.
+	 */
+	if (whereToSendOutput == DestRemote)
+		whereToSendOutput = DestNone;
+
+	ereport(WARNING,
+			(errmsg("walsender shutting down in immediate mode: replication may be incomplete")));
+
+	proc_exit(0);
+}
+
 /*
  * Shutdown if the sender is caught up.
  *
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..1b2267a1a15 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -3429,6 +3429,13 @@
   check_hook => 'check_wal_segment_size',
 },
 
+{ name => 'wal_sender_shutdown_mode', type => 'enum', context => 'PGC_USERSET', group => 'REPLICATION_SENDING',
+  short_desc => 'Sets the mode in which walsender will be terminated after shutdown request.',
+  variable => 'wal_sender_shutdown_mode',
+  boot_val => 'WALSND_SHUTDOWN_MODE_WAIT_FLUSH',
+  options => 'wal_sender_shutdown_mode_options',
+},
+
 { name => 'wal_sender_timeout', type => 'int', context => 'PGC_USERSET', group => 'REPLICATION_SENDING',
   short_desc => 'Sets the maximum time to wait for WAL replication.',
   flags => 'GUC_UNIT_MS',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 13c569d8790..017c3f98b4f 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -335,6 +335,12 @@ static const struct config_enum_entry constraint_exclusion_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry wal_sender_shutdown_mode_options[] = {
+	{"wait_flush", WALSND_SHUTDOWN_MODE_WAIT_FLUSH, false},
+	{"immediate", WALSND_SHUTDOWN_MODE_IMMEDIATE, false},
+	{NULL, 0, false}
+};
+
 /*
  * Although only "on", "off", "remote_apply", "remote_write", and "local" are
  * documented, we accept all the likely variants of "on" and "off".
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..29172585b93 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -344,6 +344,8 @@
 #max_slot_wal_keep_size = -1    # in megabytes; -1 disables
 #idle_replication_slot_timeout = 0      # in seconds; 0 disables
 #wal_sender_timeout = 60s       # in milliseconds; 0 disables
+#wal_sender_shutdown_mode = wait_flush  # walsender termination mode after
+                                # receival of shutdown request
 #track_commit_timestamp = off   # collect timestamp of transaction commit
                                 # (change requires restart)
 
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
index a4df3b8e0ae..3f962ea2792 100644
--- a/src/include/replication/walsender.h
+++ b/src/include/replication/walsender.h
@@ -24,6 +24,12 @@ typedef enum
 	CRS_USE_SNAPSHOT,
 } CRSSnapshotAction;
 
+typedef enum
+{
+	WALSND_SHUTDOWN_MODE_WAIT_FLUSH = 0,
+	WALSND_SHUTDOWN_MODE_IMMEDIATE
+} WalSndShutdownMode;
+
 /* global state */
 extern PGDLLIMPORT bool am_walsender;
 extern PGDLLIMPORT bool am_cascading_walsender;
@@ -33,6 +39,7 @@ extern PGDLLIMPORT bool wake_wal_senders;
 /* user-settable parameters */
 extern PGDLLIMPORT int max_wal_senders;
 extern PGDLLIMPORT int wal_sender_timeout;
+extern PGDLLIMPORT int wal_sender_shutdown_mode;
 extern PGDLLIMPORT bool log_replication_commands;
 
 extern void InitWalSender(void);
diff --git a/src/test/subscription/meson.build b/src/test/subscription/meson.build
index a4c7dbaff59..3b9d27fba0b 100644
--- a/src/test/subscription/meson.build
+++ b/src/test/subscription/meson.build
@@ -46,6 +46,7 @@ tests += {
       't/034_temporal.pl',
       't/035_conflicts.pl',
       't/036_sequences.pl',
+      't/037_walsnd_immediate_shutdown.pl',
       't/100_bugs.pl',
     ],
   },
diff --git a/src/test/subscription/t/037_walsnd_immediate_shutdown.pl b/src/test/subscription/t/037_walsnd_immediate_shutdown.pl
new file mode 100644
index 00000000000..c991b274cd0
--- /dev/null
+++ b/src/test/subscription/t/037_walsnd_immediate_shutdown.pl
@@ -0,0 +1,121 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Checks that publisher is able to shut down without
+# waiting for sending of all pending data to subscriber
+# with wal_sender_shutdown_mode = immediate
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+# create publisher
+my $publisher = PostgreSQL::Test::Cluster->new('publisher');
+$publisher->init(allows_streaming => 'logical');
+# set wal_sender_shutdown_mode GUC parameter to immediate
+$publisher->append_conf('postgresql.conf',
+	"wal_sender_timeout = 0
+	 wal_sender_shutdown_mode = 'immediate'");
+$publisher->start();
+
+# create subscriber
+my $subscriber = PostgreSQL::Test::Cluster->new('subscriber');
+$subscriber->init();
+$subscriber->append_conf('postgresql.conf',
+	"wal_receiver_status_interval = 1");
+$subscriber->start();
+
+# create publication for test table
+$publisher->safe_psql('postgres', q{
+	CREATE TABLE pub_test (id int PRIMARY KEY);
+	CREATE PUBLICATION pub_all FOR TABLE pub_test;
+});
+
+# create matching table on subscriber
+$subscriber->safe_psql('postgres', q{
+	CREATE TABLE pub_test (id int PRIMARY KEY);
+});
+
+# form connection string to publisher
+my $pub_connstr = $publisher->connstr;
+
+# create the subscription on subscriber
+$subscriber->safe_psql('postgres', qq{
+	CREATE SUBSCRIPTION sub_all
+	CONNECTION '$pub_connstr'
+	PUBLICATION pub_all;
+});
+
+# wait for initial sync to finish
+$subscriber->wait_for_subscription_sync($publisher, 'sub_all');
+
+# create background psql session
+my $bpgsql = $subscriber->background_psql('postgres', on_error_stop => 0);
+
+# =============================================================================
+# Testcase start: Shutdown of publisher with empty output buffers
+
+# start transaction on subscriber to hold locks
+$bpgsql->query_safe(
+	"BEGIN; INSERT INTO pub_test VALUES (0);"
+);
+
+# run concurrent transaction on publisher and commit
+$publisher->safe_psql('postgres', 'BEGIN; INSERT INTO pub_test VALUES (0); COMMIT;');
+
+# test publisher shutdown
+$publisher->stop('fast');
+pass('successfull fast shutdown of server with empty output buffers');
+
+# Testcase end: Shutdown of publisher with empty output buffers
+# =============================================================================
+
+$bpgsql->query_safe(
+	"ABORT;"
+);
+
+# restart publisher for the next testcase
+$publisher->start();
+
+$publisher->wait_for_catchup('sub_all');
+
+# =============================================================================
+# Testcase start: Shutdown of publisher with full output buffers
+
+# lock table to make apply_worker hang
+$bpgsql->query_safe(
+	"BEGIN; LOCK TABLE pub_test IN EXCLUSIVE MODE;"
+);
+
+my $last_sent_lsn = $publisher->safe_psql('postgres', "select sent_lsn from pg_stat_replication where application_name = 'sub_all';");
+my $cur_sent_lsn;
+
+# generate big amount of wal records for locked table
+$publisher->safe_psql('postgres', 'BEGIN; INSERT INTO pub_test SELECT i from generate_series(1, 25000) s(i); COMMIT;');
+
+# waiting for walsender to fill output buffers
+my $max_attempts = $PostgreSQL::Test::Utils::timeout_default;
+while ($max_attempts-- >= 0)
+{
+	sleep 1;
+
+	$cur_sent_lsn = $publisher->safe_psql('postgres', "select sent_lsn from pg_stat_replication where application_name = 'sub_all';");
+
+	my $diff = $publisher->safe_psql('postgres', qq(
+		SELECT pg_wal_lsn_diff('$cur_sent_lsn', '$last_sent_lsn');
+	));
+
+	last if $diff == 0;
+
+	$last_sent_lsn = $cur_sent_lsn;
+}
+
+# test publisher shutdown
+$publisher->stop('fast');
+pass('successfull fast shutdown of server with full output buffers');
+
+# Testcase end: Shutdown of publisher with full output buffers
+# =============================================================================
+
+done_testing();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index ddbe4c64971..6ec7fef2f29 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3330,6 +3330,7 @@ WalReceiverFunctionsType
 WalSnd
 WalSndCtlData
 WalSndSendDataCallback
+WalSndShutdownMode
 WalSndState
 WalSummarizerData
 WalSummaryFile
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-01-28 11:33  Andrey Silitskiy <[email protected]>
  parent: Ronan Dunklau <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Andrey Silitskiy @ 2026-01-28 11:33 UTC (permalink / raw)
  To: Ronan Dunklau <[email protected]>; +Cc: Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>; Fujii Masao <[email protected]>

Dear Ronan,
Thanks for participating in the discussion.

On Jan 28, 2026 Ronan Dunklau <ronan(at)dunklau(dot)fr> wrote:
 > ... a timeout for the wait (maybe named wal_sender_stop_timeout ?) would
 > allow for the same usage ...

Sounds like an option.
This is also possible in the current implementation, but your option offers
a simpler interface if we do not plan to add new wallsender completion 
modes.
The naming of the parameter is also a question, because wal_sender_timeout
already exists (which also fits the name wal_sender_stop_timeout quite 
well).
The difference between these parameters may not be obvious to users.
Thoughts?

Regards,
Andrey Silitskiy






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-02-01 19:52  Andrey Silitskiy <[email protected]>
  parent: Andrey Silitskiy <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Andrey Silitskiy @ 2026-02-01 19:52 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>; Fujii Masao <[email protected]>

 > Thanks for the review!
 > Updated the patch based on your comments.Fixed cfbot documentation 
error. Regards, Andrey Silitskiy

Attachments:

  [text/x-patch] v6-0001-Introduce-a-new-GUC-wal_sender_shutdown_mode.patch (16.1K, ../../[email protected]/3-v6-0001-Introduce-a-new-GUC-wal_sender_shutdown_mode.patch)
  download | inline diff:
From e463bbc27c338b475a2d607ad531f2d073078faf Mon Sep 17 00:00:00 2001
From: "a.silitskiy" <[email protected]>
Date: Fri, 28 Nov 2025 14:40:10 +0700
Subject: [PATCH v6] Introduce a new GUC 'wal_sender_shutdown_mode'.

Previously, at shutdown, walsender processes were always waiting to send all
pending data and ensure that all data is flushed in remote node. But in some cases
an unexpected wait may be unacceptable. For example, in logical replication,
apply_workers may hang on locks for some time, excluding the possibility of
sender's shutdown.

New guc allows to change shutdown mode of walsenders without changing
default behavior.

The shutdown modes are:

1) 'wait_flush' (the default). In this mode, the walsender will wait for all
WALs to be flushed on the receiver side, before exiting the process.

2) 'immediate'. In this mode, the walsender will exit without confirming the
remote flush. This may break the consistency between sender and receiver.
This mode might be useful for a system that has a high-latency network (to
reduce the amount of time for shutdown), or to allow the shutdown of
publisher even when when the subscriber's apply_worker is waiting for any
locks to be released.

Author: Andrey Silitskiy
Co-authored by: Hayato Kuroda
Discussion: https://postgr.es/m/TYAPR01MB586668E50FC2447AD7F92491F5E89%40TYAPR01MB5866.jpnprd01.prod.outlook.com
---
 doc/src/sgml/config.sgml                      |  30 +++++
 doc/src/sgml/high-availability.sgml           |   9 +-
 src/backend/replication/walsender.c           |  52 ++++++++
 src/backend/utils/misc/guc_parameters.dat     |   7 +
 src/backend/utils/misc/guc_tables.c           |   6 +
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/replication/walsender.h           |   7 +
 src/test/subscription/meson.build             |   1 +
 .../t/037_walsnd_immediate_shutdown.pl        | 121 ++++++++++++++++++
 src/tools/pgindent/typedefs.list              |   1 +
 10 files changed, 232 insertions(+), 4 deletions(-)
 create mode 100644 src/test/subscription/t/037_walsnd_immediate_shutdown.pl

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5560b95ee60..c8c5e736cd5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4722,6 +4722,36 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-wal_sender_shutdown_mode" xreflabel="wal_sender_shutdown_mode">
+      <term><varname>wal_sender_shutdown_mode</varname> (<type>enum</type>)
+      <indexterm>
+       <primary><varname>wal_sender_shutdown_mode</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the mode in which walsender process will terminate
+        after receival of shutdown request. Valid values are <literal>wait_flush</literal>
+        and <literal>immediate</literal>. Default value is <literal>wait_flush</literal>.
+        Can be set for each walsender.
+       </para>
+       <para>
+        In <literal>wait_flush</literal> mode, the walsender will wait for all
+        WAL data to be flushed on the receiver side, before exiting the process.
+        This helps to keep the sender and receiver in sync after shutdown, which is
+        especially important for physical replication switchovers. However,
+        it can delay server shutdown.
+       </para>
+       <para>
+        In <literal>immediate</literal> mode, the walsender will exit without waiting
+        for WAL data replication to the receiver. This can reduce shutdown time when
+        flushing WAL data to the receiver would take a long time, for example on
+        high-latency networks or when the subscriber's apply worker is blocked waiting
+        for locks in logical replication.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-synchronized-standby-slots" xreflabel="synchronized_standby_slots">
       <term><varname>synchronized_standby_slots</varname> (<type>string</type>)
       <indexterm>
diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index c3f269e0364..c4a025a0d75 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -1190,10 +1190,11 @@ primary_slot_name = 'node_a_slot'
    </para>
 
    <para>
-    Users will stop waiting if a fast shutdown is requested.  However, as
-    when using asynchronous replication, the server will not fully
-    shutdown until all outstanding WAL records are transferred to the currently
-    connected standby servers.
+    Users will stop waiting if a fast shutdown is requested.  However, if
+    <varname>wal_sender_shutdown_mode</varname> is set to <literal>wait_flush</literal>, the
+    server will not fully shutdown until all outstanding WAL records are
+    transferred to the currently connected standby servers. This waiting
+    applies to both asynchronous and synchronous replication.
    </para>
 
    </sect3>
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0e6a3d200c..80cd6086908 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -35,6 +35,8 @@
  * checkpoint finishes, the postmaster sends us SIGUSR2. This instructs
  * walsender to send any outstanding WAL, including the shutdown checkpoint
  * record, wait for it to be replicated to the standby, and then exit.
+ * If wal_sender_shutdown_mode = immediate is set, walsender process will
+ * skip these steps and terminate after receival of shutdown request.
  *
  *
  * Portions Copyright (c) 2010-2026, PostgreSQL Global Development Group
@@ -130,6 +132,9 @@ int			max_wal_senders = 10;	/* the maximum number of concurrent
 									 * walsenders */
 int			wal_sender_timeout = 60 * 1000; /* maximum time to send one WAL
 											 * data message */
+
+int			wal_sender_shutdown_mode = WALSND_SHUTDOWN_MODE_WAIT_FLUSH;
+
 bool		log_replication_commands = false;
 
 /*
@@ -262,6 +267,7 @@ static void WalSndKill(int code, Datum arg);
 pg_noreturn static void WalSndShutdown(void);
 static void XLogSendPhysical(void);
 static void XLogSendLogical(void);
+pg_noreturn static void WalSndDoneImmediate(void);
 static void WalSndDone(WalSndSendDataCallback send_data);
 static void IdentifySystem(void);
 static void UploadManifest(void);
@@ -1656,6 +1662,11 @@ ProcessPendingWrites(void)
 		/* Try to flush pending output to the client */
 		if (pq_flush_if_writable() != 0)
 			WalSndShutdown();
+
+		/* If we got shut down request in immediate shutdown mode, exit the process */
+		if ((got_STOPPING || got_SIGUSR2) &&
+			 wal_sender_shutdown_mode == WALSND_SHUTDOWN_MODE_IMMEDIATE)
+			WalSndDoneImmediate();
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -2933,6 +2944,14 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		if (pq_flush_if_writable() != 0)
 			WalSndShutdown();
 
+		/*
+		 * When immediate shutdown of walsender is requested, we do not
+		 * wait for successfull sending of all data.
+		 */
+		if ((got_STOPPING || got_SIGUSR2) &&
+			 wal_sender_shutdown_mode == WALSND_SHUTDOWN_MODE_IMMEDIATE)
+			WalSndDoneImmediate();
+
 		/* If nothing remains to be sent right now ... */
 		if (WalSndCaughtUp && !pq_is_send_pending())
 		{
@@ -3581,6 +3600,39 @@ XLogSendLogical(void)
 	}
 }
 
+/*
+ * Shutdown walsender in immediate mode.
+ */
+static void
+WalSndDoneImmediate()
+{
+	QueryCompletion qc;
+
+	/* Try to inform receiver that XLOG streaming is done */
+	SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+	EndCommand(&qc, DestRemote, false);
+
+	/*
+	 * Note that the output buffer may be full during immediate shutdown of
+	 * walsender. If pq_flush() is called at that time, the walsender process
+	 * will be stuck. Therefore, call pq_flush_if_writable() instead. Successfull
+	 * receival of done message in immediate shutdown mode is not guaranteed.
+	 */
+	pq_flush_if_writable();
+
+	/*
+	 * Prevent ereport from attempting to send any more messages to the standby.
+	 * Otherwise it can cause process stuck if output buffers are full.
+	 */
+	if (whereToSendOutput == DestRemote)
+		whereToSendOutput = DestNone;
+
+	ereport(WARNING,
+			(errmsg("walsender shutting down in immediate mode: replication may be incomplete")));
+
+	proc_exit(0);
+}
+
 /*
  * Shutdown if the sender is caught up.
  *
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..1b2267a1a15 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -3429,6 +3429,13 @@
   check_hook => 'check_wal_segment_size',
 },
 
+{ name => 'wal_sender_shutdown_mode', type => 'enum', context => 'PGC_USERSET', group => 'REPLICATION_SENDING',
+  short_desc => 'Sets the mode in which walsender will be terminated after shutdown request.',
+  variable => 'wal_sender_shutdown_mode',
+  boot_val => 'WALSND_SHUTDOWN_MODE_WAIT_FLUSH',
+  options => 'wal_sender_shutdown_mode_options',
+},
+
 { name => 'wal_sender_timeout', type => 'int', context => 'PGC_USERSET', group => 'REPLICATION_SENDING',
   short_desc => 'Sets the maximum time to wait for WAL replication.',
   flags => 'GUC_UNIT_MS',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 13c569d8790..017c3f98b4f 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -335,6 +335,12 @@ static const struct config_enum_entry constraint_exclusion_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry wal_sender_shutdown_mode_options[] = {
+	{"wait_flush", WALSND_SHUTDOWN_MODE_WAIT_FLUSH, false},
+	{"immediate", WALSND_SHUTDOWN_MODE_IMMEDIATE, false},
+	{NULL, 0, false}
+};
+
 /*
  * Although only "on", "off", "remote_apply", "remote_write", and "local" are
  * documented, we accept all the likely variants of "on" and "off".
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..29172585b93 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -344,6 +344,8 @@
 #max_slot_wal_keep_size = -1    # in megabytes; -1 disables
 #idle_replication_slot_timeout = 0      # in seconds; 0 disables
 #wal_sender_timeout = 60s       # in milliseconds; 0 disables
+#wal_sender_shutdown_mode = wait_flush  # walsender termination mode after
+                                # receival of shutdown request
 #track_commit_timestamp = off   # collect timestamp of transaction commit
                                 # (change requires restart)
 
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
index a4df3b8e0ae..3f962ea2792 100644
--- a/src/include/replication/walsender.h
+++ b/src/include/replication/walsender.h
@@ -24,6 +24,12 @@ typedef enum
 	CRS_USE_SNAPSHOT,
 } CRSSnapshotAction;
 
+typedef enum
+{
+	WALSND_SHUTDOWN_MODE_WAIT_FLUSH = 0,
+	WALSND_SHUTDOWN_MODE_IMMEDIATE
+} WalSndShutdownMode;
+
 /* global state */
 extern PGDLLIMPORT bool am_walsender;
 extern PGDLLIMPORT bool am_cascading_walsender;
@@ -33,6 +39,7 @@ extern PGDLLIMPORT bool wake_wal_senders;
 /* user-settable parameters */
 extern PGDLLIMPORT int max_wal_senders;
 extern PGDLLIMPORT int wal_sender_timeout;
+extern PGDLLIMPORT int wal_sender_shutdown_mode;
 extern PGDLLIMPORT bool log_replication_commands;
 
 extern void InitWalSender(void);
diff --git a/src/test/subscription/meson.build b/src/test/subscription/meson.build
index a4c7dbaff59..3b9d27fba0b 100644
--- a/src/test/subscription/meson.build
+++ b/src/test/subscription/meson.build
@@ -46,6 +46,7 @@ tests += {
       't/034_temporal.pl',
       't/035_conflicts.pl',
       't/036_sequences.pl',
+      't/037_walsnd_immediate_shutdown.pl',
       't/100_bugs.pl',
     ],
   },
diff --git a/src/test/subscription/t/037_walsnd_immediate_shutdown.pl b/src/test/subscription/t/037_walsnd_immediate_shutdown.pl
new file mode 100644
index 00000000000..c991b274cd0
--- /dev/null
+++ b/src/test/subscription/t/037_walsnd_immediate_shutdown.pl
@@ -0,0 +1,121 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Checks that publisher is able to shut down without
+# waiting for sending of all pending data to subscriber
+# with wal_sender_shutdown_mode = immediate
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+# create publisher
+my $publisher = PostgreSQL::Test::Cluster->new('publisher');
+$publisher->init(allows_streaming => 'logical');
+# set wal_sender_shutdown_mode GUC parameter to immediate
+$publisher->append_conf('postgresql.conf',
+	"wal_sender_timeout = 0
+	 wal_sender_shutdown_mode = 'immediate'");
+$publisher->start();
+
+# create subscriber
+my $subscriber = PostgreSQL::Test::Cluster->new('subscriber');
+$subscriber->init();
+$subscriber->append_conf('postgresql.conf',
+	"wal_receiver_status_interval = 1");
+$subscriber->start();
+
+# create publication for test table
+$publisher->safe_psql('postgres', q{
+	CREATE TABLE pub_test (id int PRIMARY KEY);
+	CREATE PUBLICATION pub_all FOR TABLE pub_test;
+});
+
+# create matching table on subscriber
+$subscriber->safe_psql('postgres', q{
+	CREATE TABLE pub_test (id int PRIMARY KEY);
+});
+
+# form connection string to publisher
+my $pub_connstr = $publisher->connstr;
+
+# create the subscription on subscriber
+$subscriber->safe_psql('postgres', qq{
+	CREATE SUBSCRIPTION sub_all
+	CONNECTION '$pub_connstr'
+	PUBLICATION pub_all;
+});
+
+# wait for initial sync to finish
+$subscriber->wait_for_subscription_sync($publisher, 'sub_all');
+
+# create background psql session
+my $bpgsql = $subscriber->background_psql('postgres', on_error_stop => 0);
+
+# =============================================================================
+# Testcase start: Shutdown of publisher with empty output buffers
+
+# start transaction on subscriber to hold locks
+$bpgsql->query_safe(
+	"BEGIN; INSERT INTO pub_test VALUES (0);"
+);
+
+# run concurrent transaction on publisher and commit
+$publisher->safe_psql('postgres', 'BEGIN; INSERT INTO pub_test VALUES (0); COMMIT;');
+
+# test publisher shutdown
+$publisher->stop('fast');
+pass('successfull fast shutdown of server with empty output buffers');
+
+# Testcase end: Shutdown of publisher with empty output buffers
+# =============================================================================
+
+$bpgsql->query_safe(
+	"ABORT;"
+);
+
+# restart publisher for the next testcase
+$publisher->start();
+
+$publisher->wait_for_catchup('sub_all');
+
+# =============================================================================
+# Testcase start: Shutdown of publisher with full output buffers
+
+# lock table to make apply_worker hang
+$bpgsql->query_safe(
+	"BEGIN; LOCK TABLE pub_test IN EXCLUSIVE MODE;"
+);
+
+my $last_sent_lsn = $publisher->safe_psql('postgres', "select sent_lsn from pg_stat_replication where application_name = 'sub_all';");
+my $cur_sent_lsn;
+
+# generate big amount of wal records for locked table
+$publisher->safe_psql('postgres', 'BEGIN; INSERT INTO pub_test SELECT i from generate_series(1, 25000) s(i); COMMIT;');
+
+# waiting for walsender to fill output buffers
+my $max_attempts = $PostgreSQL::Test::Utils::timeout_default;
+while ($max_attempts-- >= 0)
+{
+	sleep 1;
+
+	$cur_sent_lsn = $publisher->safe_psql('postgres', "select sent_lsn from pg_stat_replication where application_name = 'sub_all';");
+
+	my $diff = $publisher->safe_psql('postgres', qq(
+		SELECT pg_wal_lsn_diff('$cur_sent_lsn', '$last_sent_lsn');
+	));
+
+	last if $diff == 0;
+
+	$last_sent_lsn = $cur_sent_lsn;
+}
+
+# test publisher shutdown
+$publisher->stop('fast');
+pass('successfull fast shutdown of server with full output buffers');
+
+# Testcase end: Shutdown of publisher with full output buffers
+# =============================================================================
+
+done_testing();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index ddbe4c64971..6ec7fef2f29 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3330,6 +3330,7 @@ WalReceiverFunctionsType
 WalSnd
 WalSndCtlData
 WalSndSendDataCallback
+WalSndShutdownMode
 WalSndState
 WalSummarizerData
 WalSummaryFile
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-02-02 07:55  Michael Paquier <[email protected]>
  parent: Andrey Silitskiy <[email protected]>
  0 siblings, 2 replies; 18+ messages in thread

From: Michael Paquier @ 2026-02-02 07:55 UTC (permalink / raw)
  To: Andrey Silitskiy <[email protected]>; +Cc: pgsql-hackers; Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>; Fujii Masao <[email protected]>

On Mon, Feb 02, 2026 at 02:52:36AM +0700, Andrey Silitskiy wrote:
> +/*
> + * Shutdown walsender in immediate mode.
> + */
> +static void
> +WalSndDoneImmediate()
> +{
> +	QueryCompletion qc;
> +
> +	/* Try to inform receiver that XLOG streaming is done */
> +	SetQueryCompletion(&qc, CMDTAG_COPY, 0);
> +	EndCommand(&qc, DestRemote, false);

Couldn't that be potentially dangerous, particularly if
wal_sender_shutdown_mode is set to immediate, meaning that it applies
to all the WAL senders?  The WAL receiver side could be on a backend
with an older backend version than the WAL sender where this new GUC
exists.  Hence, a completion could be understood incorrectly by a WAL
receiver depending on how the receiving side is coded?  Assuming this
is merged into v19 in this shape, a receiver connecting to a newer
server would get a new bevahior compared to older versions, without
the receiver being aware of that.

I suspect that this option, as designed, is potentially a footgun that 
could surprise many users, especially as it is super aggressive in
shutting down everything on sight, unconditionally.
--
Michael


Attachments:

  [application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc)
  download

^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-02-02 11:58  Fujii Masao <[email protected]>
  parent: Michael Paquier <[email protected]>
  1 sibling, 0 replies; 18+ messages in thread

From: Fujii Masao @ 2026-02-02 11:58 UTC (permalink / raw)
  To: Michael Paquier <[email protected]>; +Cc: Andrey Silitskiy <[email protected]>; pgsql-hackers; Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>

On Mon, Feb 2, 2026 at 4:56 PM Michael Paquier <[email protected]> wrote:
>
> On Mon, Feb 02, 2026 at 02:52:36AM +0700, Andrey Silitskiy wrote:
> > +/*
> > + * Shutdown walsender in immediate mode.
> > + */
> > +static void
> > +WalSndDoneImmediate()
> > +{
> > +     QueryCompletion qc;
> > +
> > +     /* Try to inform receiver that XLOG streaming is done */
> > +     SetQueryCompletion(&qc, CMDTAG_COPY, 0);
> > +     EndCommand(&qc, DestRemote, false);
>
> Couldn't that be potentially dangerous, particularly if
> wal_sender_shutdown_mode is set to immediate, meaning that it applies
> to all the WAL senders?  The WAL receiver side could be on a backend
> with an older backend version than the WAL sender where this new GUC
> exists.  Hence, a completion could be understood incorrectly by a WAL
> receiver depending on how the receiving side is coded?  Assuming this
> is merged into v19 in this shape, a receiver connecting to a newer
> server would get a new bevahior compared to older versions, without
> the receiver being aware of that.
>
> I suspect that this option, as designed, is potentially a footgun that
> could surprise many users, especially as it is super aggressive in
> shutting down everything on sight, unconditionally.

I’m not sure this is actually problematic when terminating a walsender in
immediate mode while the subscriber is running an older PostgreSQL version.
I haven't been able to come up with any failure scenario yet. However,
if this does turn out to be an issue, it might be better to use the alternative
approach I mentioned in [1].

With that approach, even in immediate mode the walsender simply receives
SIGTERM and exits. This is not new behavior for walsenders, so even when
the subscriber is on an older version, seems it shouldn't risk breaking
the shutdown behavior that such a subscriber is expecting.

Regards,

[1] https://postgr.es/m/CAHGQGwGotoS0VeMDdK6ezkhvdQpWZ5oJvO3QKJKEV6Pc+rZ_9A@mail.gmail.com

-- 
Fujii Masao






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-02-04 14:03  Andrey Silitskiy <[email protected]>
  parent: Michael Paquier <[email protected]>
  1 sibling, 0 replies; 18+ messages in thread

From: Andrey Silitskiy @ 2026-02-04 14:03 UTC (permalink / raw)
  To: Michael Paquier <[email protected]>; +Cc: pgsql-hackers; Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>; Fujii Masao <[email protected]>

On Mon, Feb 2, 2026 at 4:56 PM Michael Paquier
<michael(at)paquier(dot)xyz> wrote:
 > Couldn't that be potentially dangerous, particularly if
 > wal_sender_shutdown_mode is set to immediate, meaning that it applies
 > to all the WAL senders?  The WAL receiver side could be on a backend
 > with an older backend version than the WAL sender where this new GUC
 > exists.

So far, I can't provide a scenario where the old-version receiving side is
having problems due to the behavior of immediate wal_sender_shutdown_mode.
Maybe someone else can.

Even if walsender immediate shutdown mode is set on the sender, it flushes
a done message to the receiver, as it is being done now. In the case 
when this
is not possible, it terminates in a similar way as it is currently doing
in WalSndShutdown(), the old receiver also seems to be able to handle this
case.

Regards,
Andrey Silitskiy






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-02-10 09:15  Ronan Dunklau <[email protected]>
  parent: Andrey Silitskiy <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Ronan Dunklau @ 2026-02-10 09:15 UTC (permalink / raw)
  To: Andrey Silitskiy <[email protected]>; +Cc: Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>; Greg Sabino Mullane <[email protected]>; Alexander Korotkov <[email protected]>; Fujii Masao <[email protected]>

Le mercredi 28 janvier 2026 à 12:33, Andrey Silitskiy <[email protected]> a écrit :
> This is also possible in the current implementation, but your option offers
> a simpler interface if we do not plan to add new wallsender completion
> modes.
> The naming of the parameter is also a question, because wal_sender_timeout
> already exists (which also fits the name wal_sender_stop_timeout quite
> well).
> The difference between these parameters may not be obvious to users.
> Thoughts?

The naming can probably be revisited but the use case I have in mind
is the following:

- we want to bound the time it takes to perform a restart, or a stop, with walsenders setup.
- the immediate shutdown solves this problem
- however, if one were to stop the service before doing a pg_upgrade, they would
have no other choice than either disable the behaviour entirely (by switching to
wait_flush) or fail the upgrade if a logical replication slot is pending
changes. Switching to wait_flush may be acceptable in that case, but then if the walreceiver
does not catch up in time we're back to agressively stopping postgres to abort
the waiting stop process, and the upgrade entirely.

So in my mind having a timeout makes a lot more sense.

What do you think ?






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-05-31 11:00  Alexander Lakhin <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Alexander Lakhin @ 2026-05-31 11:00 UTC (permalink / raw)
  To: Fujii Masao <[email protected]>; +Cc: Andres Freund <[email protected]>; Andrey Silitskiy <[email protected]>; Alexander Korotkov <[email protected]>; Greg Sabino Mullane <[email protected]>; Japin Li <[email protected]>; Ronan Dunklau <[email protected]>; Vitaly Davydov <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>

Hello Fujii-san,

06.04.2026 18:32, Andres Freund wrote:
> Hi,
>
> On 2026-04-06 11:40:24 +0900, Fujii Masao wrote:
>> I've pushed the patch. Thanks!
> Failed on CI just now:
>
> https://cirrus-ci.com/task/6745359004729344?logs=test_world#L410
> https://api.cirrus-ci.com/v1/artifact/task/6745359004729344/testrun/build/testrun/subscription/038_w...

A recent failure occurred on the buildfarm [1] indicates another stability
issue, this time with the test itself, I guess:
[21:12:03.301](0.068s) ok 3 - have walreceiver pid 2275585
### Stopping node "publisher" using mode fast
# Running: pg_ctl --pgdata 
/home/bf/bf-build/tamandua/HEAD/pgsql.build/testrun/subscription/038_walsnd_shutdown_timeout/data/t_038_walsnd_shutdown_timeout_publisher_data/pgdata 
--mode fast stop
waiting for server to shut down.... done
server stopped
# No postmaster PID for node "publisher"
[21:12:03.752](0.452s) not ok 4 - walsender exits due to wal_sender_shutdown_timeout even when both physical and logical 
replication are stalled
[21:12:03.753](0.001s) #   Failed test 'walsender exits due to wal_sender_shutdown_timeout even when both physical and 
logical replication are stalled'
#   at /home/bf/bf-build/tamandua/HEAD/pgsql/src/test/subscription/t/038_walsnd_shutdown_timeout.pl line 189.
### Stopping node "subscriber" using mode fast

038_walsnd_shutdown_timeout_subscriber.log doesn't really contain the
expected warning:
2026-05-29 21:11:58.777 CEST [2273817][logical replication apply worker][124/2:0] LOG:  logical replication apply worker 
for subscription "test_sub" has started
2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG: statement: BEGIN;
2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG: statement: LOCK TABLE test_tab IN EXCLUSIVE MODE;
2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG: statement: ;
2026-05-29 21:12:03.268 CEST [2273817][logical replication apply worker][124/0:0] LOG:  received message via 
replication: WARNING: replication slot "test_slot" specified in parameter "synchronized_standby_slots" does not have 
active_pid
     DETAIL:  Logical replication is waiting on the standby associated with replication slot "test_slot".
     HINT:  Start the standby associated with the replication slot "test_slot", or amend parameter 
"synchronized_standby_slots".
2026-05-29 21:12:03.433 CEST [2273817][logical replication apply worker][124/0:0] ERROR:  could not receive data from 
WAL stream: ERROR:  replication slot "test_slot" specified in parameter "synchronized_standby_slots" does not have 
active_pid
     DETAIL:  Logical replication is waiting on the standby associated with replication slot "test_slot".
     HINT:  Start the standby associated with the replication slot "test_slot", or amend parameter 
"synchronized_standby_slots".
2026-05-29 21:12:03.437 CEST [2271017][postmaster][:0] LOG: background worker "logical replication apply worker" (PID 
2273817) exited with exit code 1
2026-05-29 21:12:03.439 CEST [2275785][logical replication apply worker][125/2:0] LOG:  logical replication apply worker 
for subscription "test_sub" has started
2026-05-29 21:12:03.441 CEST [2275785][logical replication apply worker][125/0:0] ERROR:  apply worker for subscription 
"test_sub" could not connect to the publisher: connection to server on socket "/tmp/3gDgOpAsXL/.s.PGSQL.27739" failed: 
FATAL:  the database system is shutting down
2026-05-29 21:12:03.445 CEST [2271017][postmaster][:0] LOG: background worker "logical replication apply worker" (PID 
2275785) exited with exit code 1
2026-05-29 21:12:03.763 CEST [2271870][client backend][:0] LOG: disconnection: session time: 0:00:08.180 user=bf 
database=postgres host=[local]
2026-05-29 21:12:03.767 CEST [2271017][postmaster][:0] LOG: received fast shutdown request

I think this can be explained by the fact that walrcv->ready_to_display
is set before WalReceiverMain's loop reached. I've reproduced this test
failure with:
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -280,6 +280,7 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
      walrcv->sender_port = 0;
      walrcv->ready_to_display = true;
      SpinLockRelease(&walrcv->mutex);
+pg_usleep(500 * 1000);

      /* Establish the connection to the primary for XLOG streaming */
      appname = cluster_name[0] ? cluster_name : "walreceiver";


Could you please look if this can be improved?

[1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=tamandua&dt=2026-05-29%2019%3A03%3A21

Best regards,
Alexander

^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* RE: Exit walsender before confirming remote flush in logical replication
@ 2026-06-01 03:57  Hayato Kuroda (Fujitsu) <[email protected]>
  parent: Alexander Lakhin <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Hayato Kuroda (Fujitsu) @ 2026-06-01 03:57 UTC (permalink / raw)
  To: 'Alexander Lakhin' <[email protected]>; Fujii Masao <[email protected]>; +Cc: Andres Freund <[email protected]>; Andrey Silitskiy <[email protected]>; Alexander Korotkov <[email protected]>; Greg Sabino Mullane <[email protected]>; Japin Li <[email protected]>; Ronan Dunklau <[email protected]>; Vitaly Davydov <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>

Dear Alexander, Fujii-san,

> 038_walsnd_shutdown_timeout_subscriber.log doesn't really contain the
> expected warning:
> 2026-05-29 21:11:58.777 CEST [2273817][logical replication apply worker][124/2:0] LOG:  logical replication apply worker for subscription "test_sub" has started
> 2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG:  statement: BEGIN;
> 2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG:  statement: LOCK TABLE test_tab IN EXCLUSIVE MODE;
...

To confirm; IIUC the warning should be contained on the publisher log, not the
subscriber side. And below log appeared on the publisher;

```
2026-05-29 21:12:03.426 CEST [2275591][walsender][26/1:0] FATAL:  canceling authentication due to timeout
2026-05-29 21:12:03.432 CEST [2273580][checkpointer][:0] LOG:  shutting down
```

Is there a possibility that walsender was shut down during the authentication,
especially in-between BackendInitialize() and end of PerformAuthentication()?

> I think this can be explained by the fact that walrcv->ready_to_display
> is set before WalReceiverMain's loop reached. I've reproduced this test
> failure with:

Verified it could reproduce the failure, but there were no "canceling authentication
due to timeout" in the publisher log on my env.

Best regards,
Hayato Kuroda
FUJITSU LIMITED



^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-06-01 20:00  Alexander Lakhin <[email protected]>
  parent: Hayato Kuroda (Fujitsu) <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Alexander Lakhin @ 2026-06-01 20:00 UTC (permalink / raw)
  To: Hayato Kuroda (Fujitsu) <[email protected]>; Fujii Masao <[email protected]>; +Cc: Andres Freund <[email protected]>; Andrey Silitskiy <[email protected]>; Alexander Korotkov <[email protected]>; Greg Sabino Mullane <[email protected]>; Japin Li <[email protected]>; Ronan Dunklau <[email protected]>; Vitaly Davydov <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>

Dear Kuroda-san,

01.06.2026 06:57, Hayato Kuroda (Fujitsu) wrote:
>> 038_walsnd_shutdown_timeout_subscriber.log doesn't really contain the
>> expected warning:
>> 2026-05-29 21:11:58.777 CEST [2273817][logical replication apply worker][124/2:0] LOG:  logical replication apply worker for subscription "test_sub" has started
>> 2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG:  statement: BEGIN;
>> 2026-05-29 21:12:03.232 CEST [2271870][client backend][4/6:0] LOG:  statement: LOCK TABLE test_tab IN EXCLUSIVE MODE;
> ...
>
> To confirm; IIUC the warning should be contained on the publisher log, not the
> subscriber side. And below log appeared on the publisher;
>
> ```
> 2026-05-29 21:12:03.426 CEST [2275591][walsender][26/1:0] FATAL:  canceling authentication due to timeout
> 2026-05-29 21:12:03.432 CEST [2273580][checkpointer][:0] LOG:  shutting down
> ```

I'm sorry. I switched to the wrong log file during my investigation.

>
> Is there a possibility that walsender was shut down during the authentication,
> especially in-between BackendInitialize() and end of PerformAuthentication()?
>
>> I think this can be explained by the fact that walrcv->ready_to_display
>> is set before WalReceiverMain's loop reached. I've reproduced this test
>> failure with:
> Verified it could reproduce the failure, but there were no "canceling authentication
> due to timeout" in the publisher log on my env.

I think the original failure can be reproduced with:
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -645,6 +645,7 @@ ClientAuthentication(Port *port)
  #endif
      }

+if (am_walsender) pg_usleep(500 * 1000);
      if ((log_connections & LOG_CONNECTION_AUTHENTICATION) &&
          status == STATUS_OK &&
          !MyClientConnectionInfo.authn_id)

With this modification, I can see:
#   Failed test 'walsender exits due to wal_sender_shutdown_timeout even when both physical and logical replication are 
stalled'
#   at t/038_walsnd_shutdown_timeout.pl line 189.
# Looks like you failed 1 test of 4.
t/038_walsnd_shutdown_timeout.pl .. Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/4 subtests

038_walsnd_shutdown_timeout_publisher.log contains:
2026-06-01 22:32:35.166 EEST [1851879][client backend][0/0:0] FATAL:  terminating connection due to administrator command
2026-06-01 22:32:35.166 EEST [1851879][client backend][0/0:0] DETAIL:  Signal sent by PID 1851819, UID 1000.
2026-06-01 22:32:35.166 EEST [1851878][walsender][26/1:0] FATAL: canceling authentication due to timeout

Best regards,
Alexander

^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: Exit walsender before confirming remote flush in logical replication
@ 2026-06-03 10:32  Andrey Silitskiy <[email protected]>
  parent: Alexander Lakhin <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Andrey Silitskiy @ 2026-06-03 10:32 UTC (permalink / raw)
  To: Alexander Lakhin <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Fujii Masao <[email protected]>; +Cc: Andres Freund <[email protected]>; Alexander Korotkov <[email protected]>; Greg Sabino Mullane <[email protected]>; Japin Li <[email protected]>; Ronan Dunklau <[email protected]>; Vitaly Davydov <[email protected]>; Takamichi Osumi (Fujitsu) <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Kyotaro Horiguchi <[email protected]>; Peter Smith <[email protected]>

Dear Alexander, Kuroda-san,

IIUC, the problem is that the walreceiver does not reach the main loop
before it receives a signal and this leads to the absence of the required
log on publisher. So what do think of this fix?

Added a check to the test scenario that the walreceiver on standby was
fully initialized and replication started. With this fix, I can not
reproduce failure anymore.

Regards,
Andrey Silitskiy

Attachments:

  [text/x-patch] v1-0001-Fix-walsnd_shutdown_timeout-test-case.patch (2.4K, ../../[email protected]/2-v1-0001-Fix-walsnd_shutdown_timeout-test-case.patch)
  download | inline diff:
From 782c48d615abc961ab70cb33013561cedd6e6dac Mon Sep 17 00:00:00 2001
From: "a.silitskiy" <[email protected]>
Date: Wed, 3 Jun 2026 17:24:09 +0700
Subject: [PATCH v1] Fix walsnd_shutdown_timeout test case

---
 .../subscription/t/038_walsnd_shutdown_timeout.pl  | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/test/subscription/t/038_walsnd_shutdown_timeout.pl b/src/test/subscription/t/038_walsnd_shutdown_timeout.pl
index f4ed5d97852..ee5d3b77a23 100644
--- a/src/test/subscription/t/038_walsnd_shutdown_timeout.pl
+++ b/src/test/subscription/t/038_walsnd_shutdown_timeout.pl
@@ -164,16 +164,20 @@ $node_standby->append_conf(
 	hot_standby_feedback = on));
 $node_standby->start;
 
+# Wait for replication to start
+$node_publisher->safe_psql('postgres', "INSERT INTO test_tab VALUES (-1);");
+$node_standby->poll_query_until('postgres',
+ "SELECT EXISTS (SELECT 1 FROM test_tab WHERE id = -1)");
+$node_subscriber->poll_query_until('postgres',
+ "SELECT EXISTS (SELECT 1 FROM test_tab WHERE id = -1)");
+
 # Cause the logical apply worker to block on a lock by running conflicting
 # transactions on the publisher and subscriber, stalling logical replication.
-$node_publisher->wait_for_catchup('test_sub');
 $sub_session->query_safe("BEGIN; LOCK TABLE test_tab IN EXCLUSIVE MODE;");
-$node_publisher->safe_psql('postgres', "INSERT INTO test_tab VALUES (-1); ");
+$node_publisher->safe_psql('postgres', "INSERT INTO test_tab VALUES (-2); ");
 
 # Cause the standby's walreceiver to be blocked with SIGSTOP signal,
 # stalling physical replication.
-$node_standby->poll_query_until('postgres',
-	"SELECT EXISTS(SELECT 1 FROM pg_stat_wal_receiver)");
 my $receiverpid = $node_standby->safe_psql('postgres',
 	"SELECT pid FROM pg_stat_wal_receiver");
 like($receiverpid, qr/^[0-9]+$/, "have walreceiver pid $receiverpid");
@@ -184,7 +188,7 @@ $log_offset = -s $node_publisher->logfile;
 # Verify that the walsender exits due to wal_sender_shutdown_timeout
 # even when both physical and logical replication are stalled.
 # wal_sender_shutdown_timeout.
-$node_publisher->safe_psql('postgres', "INSERT INTO test_tab VALUES (-2);");
+$node_publisher->safe_psql('postgres', "INSERT INTO test_tab VALUES (-3);");
 $node_publisher->stop('fast');
 ok( $node_publisher->log_contains(
 		qr/WARNING: .* terminating walsender process due to replication shutdown timeout/,
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 18+ messages in thread


end of thread, other threads:[~2026-06-03 10:32 UTC | newest]

Thread overview: 18+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-07-24 12:58 [PATCH] Add doc details for pg_receivewal with remote_apply Jehan-Guillaume de Rorthais <[email protected]>
2023-08-23 07:25 [PATCH 1/3] Don't use LogicalRepWorker until ->in_use is verified Alvaro Herrera <[email protected]>
2023-08-23 07:25 [PATCH 1/3] Don't use LogicalRepWorker until ->in_use is verified Alvaro Herrera <[email protected]>
2026-01-20 17:03 Re: Exit walsender before confirming remote flush in logical replication Vitaly Davydov <[email protected]>
2026-01-21 15:11 ` Re: Exit walsender before confirming remote flush in logical replication Andrey Silitskiy <[email protected]>
2026-01-26 14:08   ` Re: Exit walsender before confirming remote flush in logical replication Fujii Masao <[email protected]>
2026-01-28 08:27     ` Re: Exit walsender before confirming remote flush in logical replication Ronan Dunklau <[email protected]>
2026-01-28 11:33       ` Re: Exit walsender before confirming remote flush in logical replication Andrey Silitskiy <[email protected]>
2026-02-10 09:15         ` Re: Exit walsender before confirming remote flush in logical replication Ronan Dunklau <[email protected]>
2026-01-28 11:27     ` Re: Exit walsender before confirming remote flush in logical replication Andrey Silitskiy <[email protected]>
2026-02-01 19:52       ` Re: Exit walsender before confirming remote flush in logical replication Andrey Silitskiy <[email protected]>
2026-02-02 07:55         ` Re: Exit walsender before confirming remote flush in logical replication Michael Paquier <[email protected]>
2026-02-02 11:58           ` Re: Exit walsender before confirming remote flush in logical replication Fujii Masao <[email protected]>
2026-02-04 14:03           ` Re: Exit walsender before confirming remote flush in logical replication Andrey Silitskiy <[email protected]>
2026-05-31 11:00 Re: Exit walsender before confirming remote flush in logical replication Alexander Lakhin <[email protected]>
2026-06-01 03:57 ` RE: Exit walsender before confirming remote flush in logical replication Hayato Kuroda (Fujitsu) <[email protected]>
2026-06-01 20:00   ` Re: Exit walsender before confirming remote flush in logical replication Alexander Lakhin <[email protected]>
2026-06-03 10:32     ` Re: Exit walsender before confirming remote flush in logical replication Andrey Silitskiy <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox