From 8afc7e52078ce8d85d1014bde0264523ca3f113a Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
Date: Fri, 30 Jan 2026 20:36:48 +0900
Subject: [PATCH v1] Allow walsender to send error message to client in
 non-blocking mode.

This is very WIP patch...
---
 src/backend/libpq/pqformat.c   | 18 ++++++++++++++++++
 src/backend/utils/error/elog.c | 34 ++++++++++++++++++++++++++++++++--
 src/include/libpq/pqformat.h   |  1 +
 3 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c
index ebb09db157a..4ddfbf9e335 100644
--- a/src/backend/libpq/pqformat.c
+++ b/src/backend/libpq/pqformat.c
@@ -43,6 +43,7 @@
  *		pq_sendstring	- append a null-terminated text string (with conversion)
  *		pq_send_ascii_string - append a null-terminated text string (without conversion)
  *		pq_endmessage	- send the completed message to the frontend
+ *		pq_endmessage_noblock	- like pq_endmessage, but never blocks
  * Note: it is also possible to append data to the StringInfo buffer using
  * the regular StringInfo routines, but this is discouraged since required
  * character set conversion may not occur.
@@ -302,6 +303,23 @@ pq_endmessage(StringInfo buf)
 	buf->data = NULL;
 }
 
+/* --------------------------------
+ *		pq_endmessage_noblock	- like pq_endmessage, but never blocks
+ *
+ * The data buffer is pfree()d, but if the StringInfo was allocated with
+ * makeStringInfo then the caller must still pfree it.
+ * --------------------------------
+ */
+void
+pq_endmessage_noblock(StringInfo buf)
+{
+	/* msgtype was saved in cursor field */
+	(void) pq_putmessage_noblock(buf->cursor, buf->data, buf->len);
+	/* no need to complain about any failure, since pqcomm.c already did */
+	pfree(buf->data);
+	buf->data = NULL;
+}
+
 /* --------------------------------
  *		pq_endmessage_reuse	- send the completed message to the frontend
  *
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index aa530d3685e..62152d9a50f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -3545,6 +3545,7 @@ static void
 send_message_to_frontend(ErrorData *edata)
 {
 	StringInfoData msgbuf;
+	bool		nonblocking = false;
 
 	/*
 	 * We no longer support pre-3.0 FE/BE protocol, except here.  If a client
@@ -3559,6 +3560,16 @@ send_message_to_frontend(ErrorData *edata)
 		const char *sev;
 		char		tbuf[12];
 
+		/*
+		 * When the walsender is exiting with a FATAL or PANIC, attempt to
+		 * send the error message to the client in non-blocking mode. If
+		 * sending fails (for example, because the send buffer is full), give
+		 * up sending the message and exit immediately. Without this, the
+		 * walsender could block waiting for the message to be sent, delaying
+		 * termination when it should exit immediately.
+		 */
+		nonblocking = (edata->elevel >= FATAL && MyBackendType == B_WAL_SENDER);
+
 		/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
 		if (edata->elevel < ERROR)
 			pq_beginmessage(&msgbuf, PqMsg_NoticeResponse);
@@ -3672,7 +3683,10 @@ send_message_to_frontend(ErrorData *edata)
 
 		pq_sendbyte(&msgbuf, '\0'); /* terminator */
 
-		pq_endmessage(&msgbuf);
+		if (nonblocking)
+			pq_endmessage_noblock(&msgbuf);
+		else
+			pq_endmessage(&msgbuf);
 	}
 	else
 	{
@@ -3704,7 +3718,23 @@ send_message_to_frontend(ErrorData *edata)
 	 * messages should not be a performance-critical path anyway, so an extra
 	 * flush won't hurt much ...
 	 */
-	pq_flush();
+	if (nonblocking)
+	{
+		if (pq_flush_if_writable() != 0 || pq_is_send_pending())
+		{
+			/*
+			 * Reset whereToSendOutput to prevent ereport from attempting to
+			 * send any more messages to the client.
+			 */
+			if (whereToSendOutput == DestRemote)
+				whereToSendOutput = DestNone;
+
+			ereport(COMMERROR,
+					(errmsg("could not send error message to client")));
+		}
+	}
+	else
+		pq_flush();
 }
 
 
diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h
index bc4ab1381a9..b238782d1ca 100644
--- a/src/include/libpq/pqformat.h
+++ b/src/include/libpq/pqformat.h
@@ -20,6 +20,7 @@
 extern void pq_beginmessage(StringInfo buf, char msgtype);
 extern void pq_beginmessage_reuse(StringInfo buf, char msgtype);
 extern void pq_endmessage(StringInfo buf);
+extern void pq_endmessage_noblock(StringInfo buf);
 extern void pq_endmessage_reuse(StringInfo buf);
 
 extern void pq_sendbytes(StringInfo buf, const void *data, int datalen);
-- 
2.51.2

