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
*/
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);
}
......@@ -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);
......
......@@ -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
......
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