From 1f395354d8742d57c166104874114b6e0d01e104 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 27 Nov 2023 17:42:39 +0200 Subject: [PATCH] Reduce rate of walwriter wakeups due to async commits. 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.vsc342baqs7kmbte@awork3.anarazel.de --- src/backend/access/transam/xlog.c | 49 +++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index ef9b8e4fb9..1952b013fb 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -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; -- 2.39.5