From acd4c7d58baf09fc278a0f8c1170fba2d56ccdd5 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 8 May 2012 23:05:58 -0400 Subject: [PATCH] Fix an issue in recent walwriter hibernation patch. Users of asynchronous-commit mode expect there to be a guaranteed maximum delay before an async commit's WAL records get flushed to disk. The original version of the walwriter hibernation patch broke that. Add an extra shared-memory flag to allow async commits to kick the walwriter out of hibernation mode, without adding any noticeable overhead in cases where no action is needed. --- src/backend/access/transam/xlog.c | 48 +++++++++++++++++++++++++----- src/backend/postmaster/walwriter.c | 18 +++++++++++ src/include/access/xlog.h | 1 + 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6d3a4cd3df..d3650bdf05 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -426,6 +426,13 @@ typedef struct XLogCtlData */ bool SharedHotStandbyActive; + /* + * WalWriterSleeping indicates whether the WAL writer is currently in + * low-power mode (and hence should be nudged if an async commit occurs). + * Protected by info_lck. + */ + bool WalWriterSleeping; + /* * recoveryWakeupLatch is used to wake up the startup process to continue * WAL replay, if it is waiting for WAL to arrive or failover trigger file @@ -1903,32 +1910,44 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible, bool xlog_switch) /* * Record the LSN for an asynchronous transaction commit/abort - * and nudge the WALWriter if there is a complete page to write. + * and nudge the WALWriter if there is work for it to do. * (This should not be called for synchronous commits.) */ void XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN) { XLogRecPtr WriteRqstPtr = asyncXactLSN; + bool sleeping; /* use volatile pointer to prevent code rearrangement */ volatile XLogCtlData *xlogctl = XLogCtl; SpinLockAcquire(&xlogctl->info_lck); LogwrtResult = xlogctl->LogwrtResult; + sleeping = xlogctl->WalWriterSleeping; if (XLByteLT(xlogctl->asyncXactLSN, asyncXactLSN)) xlogctl->asyncXactLSN = asyncXactLSN; SpinLockRelease(&xlogctl->info_lck); - /* back off to last completed page boundary */ - WriteRqstPtr.xrecoff -= WriteRqstPtr.xrecoff % XLOG_BLCKSZ; + /* + * 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 (!sleeping) + { + /* back off to last completed page boundary */ + WriteRqstPtr.xrecoff -= WriteRqstPtr.xrecoff % XLOG_BLCKSZ; - /* if we have already flushed that far, we're done */ - if (XLByteLE(WriteRqstPtr, LogwrtResult.Flush)) - return; + /* if we have already flushed that far, we're done */ + if (XLByteLE(WriteRqstPtr, LogwrtResult.Flush)) + return; + } /* - * Nudge the WALWriter if we have a full page of WAL to write. + * 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) SetLatch(ProcGlobal->walwriterLatch); @@ -5100,6 +5119,7 @@ XLOGShmemInit(void) XLogCtl->XLogCacheBlck = XLOGbuffers - 1; XLogCtl->SharedRecoveryInProgress = true; XLogCtl->SharedHotStandbyActive = false; + XLogCtl->WalWriterSleeping = false; XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages); SpinLockInit(&XLogCtl->info_lck); InitSharedLatch(&XLogCtl->recoveryWakeupLatch); @@ -10479,3 +10499,17 @@ WakeupRecovery(void) { SetLatch(&XLogCtl->recoveryWakeupLatch); } + +/* + * Update the WalWriterSleeping flag. + */ +void +SetWalWriterSleeping(bool sleeping) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + SpinLockAcquire(&xlogctl->info_lck); + xlogctl->WalWriterSleeping = sleeping; + SpinLockRelease(&xlogctl->info_lck); +} diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c index cd41dbbc8c..733d01fd5b 100644 --- a/src/backend/postmaster/walwriter.c +++ b/src/backend/postmaster/walwriter.c @@ -99,6 +99,7 @@ WalWriterMain(void) sigjmp_buf local_sigjmp_buf; MemoryContext walwriter_context; int left_till_hibernate; + bool hibernating; /* * If possible, make this process a group leader, so that the postmaster @@ -230,6 +231,8 @@ WalWriterMain(void) * Reset hibernation state after any error. */ left_till_hibernate = LOOPS_UNTIL_HIBERNATE; + hibernating = false; + SetWalWriterSleeping(false); /* * Advertise our latch that backends can use to wake us up while we're @@ -244,6 +247,21 @@ WalWriterMain(void) { long cur_timeout; + /* + * Advertise whether we might hibernate in this cycle. We do this + * before resetting the latch to ensure that any async commits will + * see the flag set if they might possibly need to wake us up, and + * that we won't miss any signal they send us. (If we discover work + * to do in the last cycle before we would hibernate, the global flag + * will be set unnecessarily, but little harm is done.) But avoid + * touching the global flag if it doesn't need to change. + */ + if (hibernating != (left_till_hibernate <= 1)) + { + hibernating = (left_till_hibernate <= 1); + SetWalWriterSleeping(hibernating); + } + /* Clear any already-pending wakeups */ ResetLatch(&MyProc->procLatch); diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 129712e7b9..df5f232eee 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -316,6 +316,7 @@ extern TimeLineID GetRecoveryTargetTLI(void); extern bool CheckPromoteSignal(void); extern void WakeupRecovery(void); +extern void SetWalWriterSleeping(bool sleeping); /* * Starting/stopping a base backup -- 2.40.0