From da223287c12d0077951a00f2be7eff9cdc6ae880 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Tue, 31 Jan 2023 08:23:51 +0000
Subject: [PATCH v1] Reduce power consumption by WAL writer

---
 src/backend/access/transam/xlog.c       | 55 +++++++++++++++++++++++++
 src/backend/access/transam/xloginsert.c |  6 +++
 src/backend/postmaster/walwriter.c      | 20 +++++----
 src/include/access/xlog.h               |  1 +
 4 files changed, 75 insertions(+), 7 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index fb4c860bde..918f3959d3 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -2393,6 +2393,61 @@ XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN)
 		SetLatch(ProcGlobal->walwriterLatch);
 }
 
+/*
+ * Wake up WAL writer process to write and flush newly generated WAL.
+ *
+ * This function better be in walwriter.c, but it reads LogwrtResult, a static
+ * variable from xlog.c. Hence we choose to keep it here for the sake of
+ * simplicity.
+ */
+void
+WakeupWALWriter(void)
+{
+	XLogRecPtr WriteRqstPtr = XactLastRecEnd;
+
+	/* back off to last completed page boundary */
+	WriteRqstPtr -= WriteRqstPtr % XLOG_BLCKSZ;
+
+	/*
+	 * Let's not nudge WAL writer if somebody (this backend itself in
+	 * XLogInsertRecord() or other backend or WAL writer itself in the ongoing
+	 * run) already flushed that far. This avoids unnecessary WAL writer
+	 * wakeups.
+	 *
+	 * It's possible that LogwrtResult is older, in which case, we nudge the
+	 * WAL writer and it may do nothing. This is okay than to make this
+	 * function costly by acquiring info_lck and read LogwrtResult freshly.
+	 */
+	if (WriteRqstPtr <= LogwrtResult.Flush)
+		return;
+
+	/*
+	 * XXXX: While more number of nudges/SetLatch() calls reduce the amount of
+	 * WAL that this backend needs to write/flsuh, it might burden the WAL
+	 * writer too much.
+	 *
+	 * To reduce the number of nudges, we can do all or some of the following
+	 * things:
+	 *
+	 * 1) Nudge only when WAL writer is sleeping (use WalWriterSleeping).
+	 *
+	 * 2) Nudge only after wal_writer_delay has elapsed since the last nudge.
+	 *
+	 * 3) Nudge only after this backend has generated a configurable amount of
+	 *    WAL. Introduce a new GUC for this purpose wal_writer_wakeup_after
+	 *    (Amount of WAL written out by a backend that wakes up WAL writer to
+	 *    help write and flush WAL). Or use existing GUC
+	 *    wal_writer_flush_after.
+	 */
+
+	/*
+	 * Nudge WAL writer. Even if WAL writer is woken up, it does its work only
+	 * when necessary at its own pace, see XLogBackgroundFlush() for details.
+	 */
+	if (ProcGlobal->walwriterLatch)
+		SetLatch(ProcGlobal->walwriterLatch);
+}
+
 /*
  * Record the LSN up to which we can remove WAL because it's not required by
  * any replication slot.
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 008612e032..3d7dfec3ba 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -503,6 +503,12 @@ XLogInsert(RmgrId rmid, uint8 info)
 
 	XLogResetInsertion();
 
+	/*
+	 * XXX: Effects of calling WakeupWALWriter() in XLogInsert() needs to be
+	 * measured, say with high-write workload.
+	 */
+	WakeupWALWriter();
+
 	return EndPos;
 }
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 3113e8fbdd..9e30679cdc 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -71,12 +71,10 @@ int			WalWriterDelay = 200;
 int			WalWriterFlushAfter = 128;
 
 /*
- * Number of do-nothing loops before lengthening the delay time, and the
- * multiplier to apply to WalWriterDelay when we do decide to hibernate.
+ * Number of do-nothing loops before lengthening the delay time.
  * (Perhaps these need to be configurable?)
  */
 #define LOOPS_UNTIL_HIBERNATE		50
-#define HIBERNATE_FACTOR			25
 
 /* Prototypes for private functions */
 static void HandleWalWriterInterrupts(void);
@@ -226,6 +224,7 @@ WalWriterMain(void)
 	for (;;)
 	{
 		long		cur_timeout;
+		int			wakeEvents;
 
 		/*
 		 * Advertise whether we might hibernate in this cycle.  We do this
@@ -262,16 +261,23 @@ WalWriterMain(void)
 
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
-		 * haven't done anything useful for quite some time, lengthen the
-		 * sleep time so as to reduce the server's idle power consumption.
+		 * haven't done anything useful for quite some time, sleep until an
+		 * event occurs so as to reduce the server's idle power consumption.
 		 */
 		if (left_till_hibernate > 0)
+		{
 			cur_timeout = WalWriterDelay;	/* in ms */
+			wakeEvents = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
+		}
 		else
-			cur_timeout = WalWriterDelay * HIBERNATE_FACTOR;
+		{
+			Assert(hibernating == true);
+			cur_timeout = -1L;
+			wakeEvents = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
+		}
 
 		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						 wakeEvents,
 						 cur_timeout,
 						 WAIT_EVENT_WAL_WRITER_MAIN);
 	}
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index cfe5409738..f30decfb7a 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -207,6 +207,7 @@ extern int	XLogFileOpen(XLogSegNo segno, TimeLineID tli);
 extern void CheckXLogRemoved(XLogSegNo segno, TimeLineID tli);
 extern XLogSegNo XLogGetLastRemovedSegno(void);
 extern void XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN);
+extern void WakeupWALWriter(void);
 extern void XLogSetReplicationSlotMinimumLSN(XLogRecPtr lsn);
 
 extern void xlog_redo(struct XLogReaderState *record);
-- 
2.34.1

