From b4e067f3ba003cf88e8390406601328443b43a7e Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Tue, 25 Nov 2025 15:59:06 -0800
Subject: [PATCH v30 3/3] PoC: Ensure XLogLogicalInfoActive() returns
 consistent result.

---
 src/backend/access/transam/xact.c            |  3 ++
 src/backend/replication/logical/logicalctl.c | 44 ++++++++++++++++++++
 src/include/access/xlog.h                    | 21 ++++++----
 src/include/replication/logicalctl.h         |  2 +
 4 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index d2d3c9b6ed6..02b4328b011 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -2489,6 +2489,7 @@ CommitTransaction(void)
 	AtEOXact_Snapshot(true, false);
 	AtEOXact_ApplyLauncher(true);
 	AtEOXact_LogicalRepWorkers(true);
+	AtEOXact_LogicalCtl();
 	pgstat_report_xact_timestamp(0);
 
 	ResourceOwnerDelete(TopTransactionResourceOwner);
@@ -2784,6 +2785,7 @@ PrepareTransaction(void)
 	/* we treat PREPARE as ROLLBACK so far as waking workers goes */
 	AtEOXact_ApplyLauncher(false);
 	AtEOXact_LogicalRepWorkers(false);
+	AtEOXact_LogicalCtl();
 	pgstat_report_xact_timestamp(0);
 
 	CurrentResourceOwner = NULL;
@@ -3011,6 +3013,7 @@ AbortTransaction(void)
 		AtEOXact_PgStat(false, is_parallel_worker);
 		AtEOXact_ApplyLauncher(false);
 		AtEOXact_LogicalRepWorkers(false);
+		AtEOXact_LogicalCtl();
 		pgstat_report_xact_timestamp(0);
 	}
 
diff --git a/src/backend/replication/logical/logicalctl.c b/src/backend/replication/logical/logicalctl.c
index aee45c790d9..738afae04da 100644
--- a/src/backend/replication/logical/logicalctl.c
+++ b/src/backend/replication/logical/logicalctl.c
@@ -108,6 +108,17 @@ static LogicalDecodingCtlData *LogicalDecodingCtl = NULL;
  */
 bool		XLogLogicalInfo = false;
 
+/*
+ * A transaction local cache of XLogLogicalInfo:
+ *		-1: not cached yet, need to check XLogLogicalInfo.
+ *		0: cached, XLogLogicalInfo is disabled.
+ *		1: cached, XLogLogicalInfo is enabled.
+ * The cache is set when checking XLogLogicalInfoActive() for the first time
+ * in the transaction and kept until the transaction end. Once the flag value
+ * is cached, this cache is used simply by casting to an boolean.
+ */
+int			XLogLogicalInfoXactCache = -1;
+
 static void update_xlog_logical_info(void);
 static void abort_logical_decoding_activation(int code, Datum arg);
 static void write_logical_decoding_status_update_record(bool status);
@@ -213,6 +224,39 @@ IsXLogLogicalInfoEnabled(void)
 	return xlog_logical_info;
 }
 
+/*
+ * Reset the local cache at end of the transaction.
+ */
+void
+AtEOXact_LogicalCtl(void)
+{
+	XLogLogicalInfoXactCache = -1;
+}
+
+/*
+ * Sub-routine for XLogLogicalInfoActive() to check if logical WAL logging
+ * is enabled. To save function call overheads, the caller must check
+ * the XLogLogicalInfoXactCache first, and use this function only when it's
+ * not set.
+ */
+bool
+CheckXLogLogicalInfoSlow(void)
+{
+	/* Returns the current state if we're out of the transaction */
+	if (!IsTransactionState())
+		return XLogLogicalInfo;
+
+	Assert(XLogLogicalInfoXactCache == -1);
+
+	/*
+	 * Cache the current state so as to return the same result within the same
+	 * transaction.
+	 */
+	XLogLogicalInfoXactCache = (int) XLogLogicalInfo;
+
+	return XLogLogicalInfoXactCache;
+}
+
 /*
  * Enable or disable both the status of logical info WAL logging and logical
  * decoding in shmem.
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 58655d91db7..fded25aba3d 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -13,6 +13,7 @@
 
 #include "access/xlogbackup.h"
 #include "access/xlogdefs.h"
+#include "replication/logicalctl.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
@@ -97,6 +98,7 @@ extern PGDLLIMPORT int wal_level;
 extern PGDLLEXPORT int effective_wal_level;
 
 extern PGDLLEXPORT bool XLogLogicalInfo;
+extern PGDLLEXPORT int XLogLogicalInfoXactCache;
 
 /* Is WAL archiving enabled (always or only while server is running normally)? */
 #define XLogArchivingActive() \
@@ -128,14 +130,19 @@ extern PGDLLEXPORT bool XLogLogicalInfo;
 /*
  * Do we need to WAL-log information required only for logical replication?
  *
- * When XLogLogicalInfo is true, it enables logical-decoding-related WAL logging
- * as if wal_level were set to 'logical', even if it's actually set to 'replica'.
- * XLogLogicalInfo is a process-local variable, so the value returned by this
- * macro might not reflect the latest state, but is sufficient for process-local
- * WAL-logging decisions. See comments atop logicalctl.c for details on controlling
- * the effective_wal_level.
+ * When XLogLogicalInfoActive() returns true, it enables logical-decoding-related
+ * WAL logging as if wal_level were set to 'logical', even if it's actually set
+ * to 'replica'. The XLogLogicalInfoXactCache is a transaction-local cache so
+ * that this macro ensures to return the same result within the same transaction.
+ * Also, it might not reflect the latest state as it's a process local cache,
+ * but it is sufficient for process-local WAL-logging decisions. See comments
+ * atop logicalctl.c for details on controlling the effective_wal_level.
  */
-#define XLogLogicalInfoActive() (wal_level >= WAL_LEVEL_LOGICAL || XLogLogicalInfo)
+#define XLogLogicalInfoActive() \
+	(wal_level >= WAL_LEVEL_LOGICAL || \
+	 (unlikely(XLogLogicalInfoXactCache < 0) ? \
+	  CheckXLogLogicalInfoSlow() : \
+	  (bool) XLogLogicalInfoXactCache))
 
 #ifdef WAL_DEBUG
 extern PGDLLIMPORT bool XLOG_DEBUG;
diff --git a/src/include/replication/logicalctl.h b/src/include/replication/logicalctl.h
index 77ded671473..06423b2ee75 100644
--- a/src/include/replication/logicalctl.h
+++ b/src/include/replication/logicalctl.h
@@ -21,6 +21,8 @@ extern void InitializeProcessXLogLogicalInfo(void);
 extern bool ProcessBarrierUpdateXLogLogicalInfo(void);
 extern bool IsLogicalDecodingEnabled(void);
 extern bool IsXLogLogicalInfoEnabled(void);
+extern bool CheckXLogLogicalInfoSlow(void);
+extern void AtEOXact_LogicalCtl(void);
 extern void EnsureLogicalDecodingEnabled(void);
 extern void RequestDisableLogicalDecoding(void);
 extern void DisableLogicalDecodingIfNecessary(void);
-- 
2.47.3

