Reduce rate of walwriter wakeups due to async commits.
authorHeikki Linnakangas <[email protected]>
Mon, 27 Nov 2023 15:42:39 +0000 (17:42 +0200)
committerHeikki Linnakangas <[email protected]>
Mon, 27 Nov 2023 15:42:39 +0000 (17:42 +0200)
XLogSetAsyncXactLSN(), called at asynchronous commit, would wake up
walwriter every time the LSN advances, but walwriter doesn't actually
do anything unless it has at least 'wal_writer_flush_after' full
blocks of WAL to write. Repeatedly waking up walwriter to do nothing
is a waste of CPU cycles in both walwriter and the backends doing the
wakeups. To fix, apply the same logic in XLogSetAsyncXactLSN() to
decide whether to wake up walwriter, as walwriter uses to determine if
it has any work to do.

In the passing, rename misleadingly named 'flushbytes' local variable
to 'flushblocks'.

Author: Andres Freund, Heikki Linnakangas
Discussion: https://www.postgresql.org/message-id/20231024230929[email protected]

src/backend/access/transam/xlog.c

index ef9b8e4fb9688ab4f7247b067b2ecf35772c8da6..1952b013fb4dd911512bc1a452cad66e176d16eb 100644 (file)
@@ -2456,35 +2456,44 @@ XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN)
 {
    XLogRecPtr  WriteRqstPtr = asyncXactLSN;
    bool        sleeping;
+   bool        wakeup = false;
+   XLogRecPtr  prevAsyncXactLSN;
 
    SpinLockAcquire(&XLogCtl->info_lck);
    LogwrtResult = XLogCtl->LogwrtResult;
    sleeping = XLogCtl->WalWriterSleeping;
+   prevAsyncXactLSN = XLogCtl->asyncXactLSN;
    if (XLogCtl->asyncXactLSN < asyncXactLSN)
        XLogCtl->asyncXactLSN = asyncXactLSN;
    SpinLockRelease(&XLogCtl->info_lck);
 
    /*
-    * If the WALWriter is sleeping, we should kick it to make it come out of
-    * low-power mode.  Otherwise, determine whether there's a full page of
-    * WAL available to write.
+    * If somebody else already called this function with a more aggressive
+    * LSN, they will have done what we needed (and perhaps more).
     */
-   if (!sleeping)
+   if (asyncXactLSN <= prevAsyncXactLSN)
+       return;
+
+   /*
+    * If the WALWriter is sleeping, kick it to make it come out of low-power
+    * mode, so that this async commit will reach disk within the expected
+    * amount of time.  Otherwise, determine whether it has enough WAL
+    * available to flush, the same way that XLogBackgroundFlush() does.
+    */
+   if (sleeping)
+       wakeup = true;
+   else
    {
-       /* back off to last completed page boundary */
-       WriteRqstPtr -= WriteRqstPtr % XLOG_BLCKSZ;
+       int         flushblocks;
 
-       /* if we have already flushed that far, we're done */
-       if (WriteRqstPtr <= LogwrtResult.Flush)
-           return;
+       flushblocks =
+           WriteRqstPtr / XLOG_BLCKSZ - LogwrtResult.Flush / XLOG_BLCKSZ;
+
+       if (WalWriterFlushAfter == 0 || flushblocks >= WalWriterFlushAfter)
+           wakeup = true;
    }
 
-   /*
-    * Nudge the WALWriter: it has a full page of WAL to write, or we want it
-    * to come out of low-power mode so that this async commit will reach disk
-    * within the expected amount of time.
-    */
-   if (ProcGlobal->walwriterLatch)
+   if (wakeup && ProcGlobal->walwriterLatch)
        SetLatch(ProcGlobal->walwriterLatch);
 }
 
@@ -2803,7 +2812,7 @@ XLogBackgroundFlush(void)
    bool        flexible = true;
    static TimestampTz lastflush;
    TimestampTz now;
-   int         flushbytes;
+   int         flushblocks;
    TimeLineID  insertTLI;
 
    /* XLOG doesn't need flushing during recovery */
@@ -2855,9 +2864,13 @@ XLogBackgroundFlush(void)
    /*
     * Determine how far to flush WAL, based on the wal_writer_delay and
     * wal_writer_flush_after GUCs.
+    *
+    * Note that XLogSetAsyncXactLSN() performs similar calculation based on
+    * wal_writer_flush_after, to decide when to wake us up.  Make sure the
+    * logic is the same in both places if you change this.
     */
    now = GetCurrentTimestamp();
-   flushbytes =
+   flushblocks =
        WriteRqst.Write / XLOG_BLCKSZ - LogwrtResult.Flush / XLOG_BLCKSZ;
 
    if (WalWriterFlushAfter == 0 || lastflush == 0)
@@ -2876,7 +2889,7 @@ XLogBackgroundFlush(void)
        WriteRqst.Flush = WriteRqst.Write;
        lastflush = now;
    }
-   else if (flushbytes >= WalWriterFlushAfter)
+   else if (flushblocks >= WalWriterFlushAfter)
    {
        /* exceeded wal_writer_flush_after blocks, flush */
        WriteRqst.Flush = WriteRqst.Write;