Commit acd4c7d5 authored by Tom Lane's avatar Tom Lane

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.
parent 8b77e226
...@@ -426,6 +426,13 @@ typedef struct XLogCtlData ...@@ -426,6 +426,13 @@ typedef struct XLogCtlData
*/ */
bool SharedHotStandbyActive; 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 * 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 * 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) ...@@ -1903,32 +1910,44 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible, bool xlog_switch)
/* /*
* Record the LSN for an asynchronous transaction commit/abort * 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.) * (This should not be called for synchronous commits.)
*/ */
void void
XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN) XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN)
{ {
XLogRecPtr WriteRqstPtr = asyncXactLSN; XLogRecPtr WriteRqstPtr = asyncXactLSN;
bool sleeping;
/* use volatile pointer to prevent code rearrangement */ /* use volatile pointer to prevent code rearrangement */
volatile XLogCtlData *xlogctl = XLogCtl; volatile XLogCtlData *xlogctl = XLogCtl;
SpinLockAcquire(&xlogctl->info_lck); SpinLockAcquire(&xlogctl->info_lck);
LogwrtResult = xlogctl->LogwrtResult; LogwrtResult = xlogctl->LogwrtResult;
sleeping = xlogctl->WalWriterSleeping;
if (XLByteLT(xlogctl->asyncXactLSN, asyncXactLSN)) if (XLByteLT(xlogctl->asyncXactLSN, asyncXactLSN))
xlogctl->asyncXactLSN = asyncXactLSN; xlogctl->asyncXactLSN = asyncXactLSN;
SpinLockRelease(&xlogctl->info_lck); 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 we have already flushed that far, we're done */
if (XLByteLE(WriteRqstPtr, LogwrtResult.Flush)) if (XLByteLE(WriteRqstPtr, LogwrtResult.Flush))
return; 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) if (ProcGlobal->walwriterLatch)
SetLatch(ProcGlobal->walwriterLatch); SetLatch(ProcGlobal->walwriterLatch);
...@@ -5100,6 +5119,7 @@ XLOGShmemInit(void) ...@@ -5100,6 +5119,7 @@ XLOGShmemInit(void)
XLogCtl->XLogCacheBlck = XLOGbuffers - 1; XLogCtl->XLogCacheBlck = XLOGbuffers - 1;
XLogCtl->SharedRecoveryInProgress = true; XLogCtl->SharedRecoveryInProgress = true;
XLogCtl->SharedHotStandbyActive = false; XLogCtl->SharedHotStandbyActive = false;
XLogCtl->WalWriterSleeping = false;
XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages); XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages);
SpinLockInit(&XLogCtl->info_lck); SpinLockInit(&XLogCtl->info_lck);
InitSharedLatch(&XLogCtl->recoveryWakeupLatch); InitSharedLatch(&XLogCtl->recoveryWakeupLatch);
...@@ -10479,3 +10499,17 @@ WakeupRecovery(void) ...@@ -10479,3 +10499,17 @@ WakeupRecovery(void)
{ {
SetLatch(&XLogCtl->recoveryWakeupLatch); 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);
}
...@@ -99,6 +99,7 @@ WalWriterMain(void) ...@@ -99,6 +99,7 @@ WalWriterMain(void)
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
MemoryContext walwriter_context; MemoryContext walwriter_context;
int left_till_hibernate; int left_till_hibernate;
bool hibernating;
/* /*
* If possible, make this process a group leader, so that the postmaster * If possible, make this process a group leader, so that the postmaster
...@@ -230,6 +231,8 @@ WalWriterMain(void) ...@@ -230,6 +231,8 @@ WalWriterMain(void)
* Reset hibernation state after any error. * Reset hibernation state after any error.
*/ */
left_till_hibernate = LOOPS_UNTIL_HIBERNATE; left_till_hibernate = LOOPS_UNTIL_HIBERNATE;
hibernating = false;
SetWalWriterSleeping(false);
/* /*
* Advertise our latch that backends can use to wake us up while we're * Advertise our latch that backends can use to wake us up while we're
...@@ -244,6 +247,21 @@ WalWriterMain(void) ...@@ -244,6 +247,21 @@ WalWriterMain(void)
{ {
long cur_timeout; 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 */ /* Clear any already-pending wakeups */
ResetLatch(&MyProc->procLatch); ResetLatch(&MyProc->procLatch);
......
...@@ -316,6 +316,7 @@ extern TimeLineID GetRecoveryTargetTLI(void); ...@@ -316,6 +316,7 @@ extern TimeLineID GetRecoveryTargetTLI(void);
extern bool CheckPromoteSignal(void); extern bool CheckPromoteSignal(void);
extern void WakeupRecovery(void); extern void WakeupRecovery(void);
extern void SetWalWriterSleeping(bool sleeping);
/* /*
* Starting/stopping a base backup * Starting/stopping a base backup
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment