diff src/backend/access/transam/xlog.c index 8d0aabf..e5adfab *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** typedef struct XLogCtlData *** 434,444 **** Latch recoveryWakeupLatch; /* - * WALWriterLatch is used to wake up the WALWriter to write some WAL. - */ - Latch WALWriterLatch; - - /* * During recovery, we keep a copy of the latest checkpoint record here. * Used by the background writer when it wants to create a restartpoint. * --- 434,439 ---- *************** begin:; *** 1213,1218 **** --- 1208,1220 ---- END_CRIT_SECTION(); + /* + * Now that we've gone through with inserting WAL, nudge the WALWriter, + * provoking a set number of iterations. + */ + if (ProcGlobal->walwriterLatch) + SetLatch(ProcGlobal->walwriterLatch); + return RecPtr; } *************** XLogSetAsyncXactLSN(XLogRecPtr asyncXact *** 1935,1941 **** /* * Nudge the WALWriter if we have a full page of WAL to write. */ ! SetLatch(&XLogCtl->WALWriterLatch); } /* --- 1937,1944 ---- /* * Nudge the WALWriter if we have a full page of WAL to write. */ ! if (ProcGlobal->walwriterLatch) ! SetLatch(ProcGlobal->walwriterLatch); } /* *************** XLogFlush(XLogRecPtr record) *** 2173,2188 **** * case for async commits.) * * This routine is invoked periodically by the background walwriter process. */ ! void XLogBackgroundFlush(void) { XLogRecPtr WriteRqstPtr; bool flexible = true; /* XLOG doesn't need flushing during recovery */ if (RecoveryInProgress()) ! return; /* read LogwrtResult and update local state */ { --- 2176,2194 ---- * case for async commits.) * * This routine is invoked periodically by the background walwriter process. + * + * Returns a value indicating if useful work was performed. */ ! bool XLogBackgroundFlush(void) { XLogRecPtr WriteRqstPtr; bool flexible = true; + bool worked = false; /* XLOG doesn't need flushing during recovery */ if (RecoveryInProgress()) ! return false; /* read LogwrtResult and update local state */ { *************** XLogBackgroundFlush(void) *** 2224,2230 **** XLogFileClose(); } } ! return; } #ifdef WAL_DEBUG --- 2230,2236 ---- XLogFileClose(); } } ! return false; } #ifdef WAL_DEBUG *************** XLogBackgroundFlush(void) *** 2247,2256 **** --- 2253,2265 ---- WriteRqst.Write = WriteRqstPtr; WriteRqst.Flush = WriteRqstPtr; XLogWrite(WriteRqst, flexible, false); + worked = true; } LWLockRelease(WALWriteLock); END_CRIT_SECTION(); + + return worked; } /* *************** XLOGShmemInit(void) *** 5101,5107 **** XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages); SpinLockInit(&XLogCtl->info_lck); InitSharedLatch(&XLogCtl->recoveryWakeupLatch); - InitSharedLatch(&XLogCtl->WALWriterLatch); /* * If we are not in bootstrap mode, pg_control should already exist. Read --- 5110,5115 ---- *************** WakeupRecovery(void) *** 10479,10490 **** { SetLatch(&XLogCtl->recoveryWakeupLatch); } - - /* - * Manage the WALWriterLatch - */ - Latch * - WALWriterLatch(void) - { - return &XLogCtl->WALWriterLatch; - } --- 10487,10489 ---- diff src/backend/postmaster/walwriter.c index 08ef946..eb09359 *** a/src/backend/postmaster/walwriter.c --- b/src/backend/postmaster/walwriter.c *************** *** 54,59 **** --- 54,60 ---- #include "storage/ipc.h" #include "storage/lwlock.h" #include "storage/pmsignal.h" + #include "storage/proc.h" #include "storage/smgr.h" #include "utils/guc.h" #include "utils/hsearch.h" *************** *** 67,81 **** --- 68,93 ---- int WalWriterDelay = 200; /* + * Number of loops before each nap is extended to a hibernation, and time to + * sleep between walwriter rounds when hibernating. + */ + #define LOOPS_UNTIL_HIBERNATE 100 + #define HIBERNATE_MS 10000 + + /* * Flags set by interrupt handlers for later service in the main loop. */ static volatile sig_atomic_t got_SIGHUP = false; static volatile sig_atomic_t shutdown_requested = false; + /* Prototypes for private functions */ + static bool WalWriterNap(bool hibernating); + /* Signal handlers */ static void wal_quickdie(SIGNAL_ARGS); static void WalSigHupHandler(SIGNAL_ARGS); static void WalShutdownHandler(SIGNAL_ARGS); + static void walwriter_sigusr1_handler(SIGNAL_ARGS); /* * Main entry point for walwriter process *************** WalWriterMain(void) *** 88,95 **** { sigjmp_buf local_sigjmp_buf; MemoryContext walwriter_context; ! ! InitLatch(WALWriterLatch()); /* initialize latch used in main loop */ /* * If possible, make this process a group leader, so that the postmaster --- 100,107 ---- { sigjmp_buf local_sigjmp_buf; MemoryContext walwriter_context; ! int left_till_hibernate = LOOPS_UNTIL_HIBERNATE; ! bool hibernating; /* * If possible, make this process a group leader, so that the postmaster *************** WalWriterMain(void) *** 114,120 **** pqsignal(SIGQUIT, wal_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, SIG_IGN); /* reserve for ProcSignal */ pqsignal(SIGUSR2, SIG_IGN); /* not used */ /* --- 126,132 ---- pqsignal(SIGQUIT, wal_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, walwriter_sigusr1_handler); pqsignal(SIGUSR2, SIG_IGN); /* not used */ /* *************** WalWriterMain(void) *** 218,229 **** PG_SETMASK(&UnBlockSig); /* * Loop forever */ for (;;) { - ResetLatch(WALWriterLatch()); - /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. --- 230,246 ---- PG_SETMASK(&UnBlockSig); /* + * Advertise our latch that backends can use to wake us up while we're + * sleeping. + */ + ProcGlobal->walwriterLatch = &MyProc->procLatch; + + /* * Loop forever */ + hibernating = false; for (;;) { /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. *************** WalWriterMain(void) *** 248,261 **** /* * Do what we're here for... */ ! XLogBackgroundFlush(); ! (void) WaitLatch(WALWriterLatch(), ! WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, ! WalWriterDelay /* ms */); } } /* -------------------------------- * signal handler routines --- 265,394 ---- /* * Do what we're here for... */ ! if (hibernating) ! ResetLatch(&MyProc->procLatch); ! /* ! * If XLogBackgroundFlush() found useful work to do, reset iterations ! * left until hibernation. ! */ ! if (XLogBackgroundFlush()) ! left_till_hibernate = LOOPS_UNTIL_HIBERNATE; ! else if (left_till_hibernate > 0) ! left_till_hibernate--; ! ! if (left_till_hibernate == 0 && !hibernating) ! { ! /* ! * XLogBackgroundFlush did nothing for a sufficient number of ! * iterations. Since there doesn't seem to be any work for the ! * walwriter to do, go into slower-paced "hibernation" mode, where ! * we sleep for much longer times than bgwriter_delay says. Fewer ! * wakeups saves electricity. If a backend inserts WAL, it will ! * wake us up by setting our latch. ! * ! * The latch is kept set during productive cycles where WAL is ! * written and/or synced, and only reset before going into a longer ! * sleep. That ensures that when there's a constant trickle of ! * activity, the SetLatch() calls that backends have to do will see ! * the latch as already set, and are not slowed down by having to ! * actually set the latch and signal us. ! */ ! hibernating = true; ! ! /* ! * Take one more short nap and perform one more walwriter cycle - ! * control in another process might have reached XLogInsert() just ! * after we finished the previous walwriter cycle, while the latch ! * was still set. If we still find nothing to do after this cycle, ! * the next sleep will be longer. ! */ ! WalWriterNap(false); ! continue; ! } ! else if (left_till_hibernate > 0 && hibernating) ! { ! /* ! * Woke up from hibernation, and may have found work to do. Set the ! * latch just in case it's not set yet (usually we wake up from ! * hibernation because a backend already set the latch, but not ! * necessarily). ! */ ! SetLatch(&MyProc->procLatch); ! hibernating = false; ! } ! ! /* ! * If the latch is set, don't immediately go back to hibernation if no ! * work is performed in the first iteration. ! */ ! if (WalWriterNap(hibernating)) ! left_till_hibernate = LOOPS_UNTIL_HIBERNATE; } } + /* + * WalWriterNap -- Nap for the configured time or until a signal is received. + * + * If 'hibernating' is false, sleeps for wal_writer_delay milliseconds. + * Otherwise sleeps longer, but also wakes up if the process latch is set. + * + * Returns a value indicating if return was due to latch being set. + */ + static bool + WalWriterNap(bool hibernating) + { + long udelay; + bool latch_set = false; + + /* + * Nap for the configured time, or sleep for 10 seconds if there is no + * walwriter activity required. + * + * If there was no work to do in the previous LOOPS_UNTIL_HIBERNATE cycles, + * take a longer nap. + */ + if (hibernating) + { + int res = WaitLatch(&MyProc->procLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, + HIBERNATE_MS); + + /* + * Only do a quick return if timeout was reached (or postmaster died) + * to ensure that no less than WalWriterDelay ms has passed between + * XLogBackgroundFlush calls - WaitLatch() might have returned + * instantaneously. + */ + if (res & (WL_TIMEOUT | WL_POSTMASTER_DEATH)) + return false; + + /* WL_LATCH_SET is documented as reliable */ + latch_set = (res & WL_LATCH_SET) != 0; + } + + /* + * Nap for the configured time. + * + * On some platforms, signals won't interrupt the sleep. To ensure we + * respond reasonably promptly when someone signals us, break down the + * sleep into 1-second increments, and check for interrupts after each + * nap. + */ + udelay = WalWriterDelay * 1000L; + + while (udelay > 999999L) + { + if (got_SIGHUP || shutdown_requested) + break; + pg_usleep(1000000L); + udelay -= 1000000L; + } + if (!(got_SIGHUP || shutdown_requested)) + pg_usleep(udelay); + + return latch_set; + } /* -------------------------------- * signal handler routines *************** wal_quickdie(SIGNAL_ARGS) *** 298,311 **** static void WalSigHupHandler(SIGNAL_ARGS) { got_SIGHUP = true; ! SetLatch(WALWriterLatch()); } /* SIGTERM: set flag to exit normally */ static void WalShutdownHandler(SIGNAL_ARGS) { shutdown_requested = true; ! SetLatch(WALWriterLatch()); } --- 431,465 ---- static void WalSigHupHandler(SIGNAL_ARGS) { + int save_errno = errno; + got_SIGHUP = true; ! if (MyProc) ! SetLatch(&MyProc->procLatch); ! ! errno = save_errno; } /* SIGTERM: set flag to exit normally */ static void WalShutdownHandler(SIGNAL_ARGS) { + int save_errno = errno; + shutdown_requested = true; ! if (MyProc) ! SetLatch(&MyProc->procLatch); ! ! errno = save_errno; ! } ! ! /* SIGUSR1: used for latch wakeups */ ! static void ! walwriter_sigusr1_handler(SIGNAL_ARGS) ! { ! int save_errno = errno; ! ! latch_sigusr1_handler(); ! ! errno = save_errno; } diff src/backend/storage/lmgr/proc.c index d1bf264..43c4846 *** a/src/backend/storage/lmgr/proc.c --- b/src/backend/storage/lmgr/proc.c *************** InitProcGlobal(void) *** 187,192 **** --- 187,193 ---- ProcGlobal->startupProcPid = 0; ProcGlobal->startupBufferPinWaitBufId = -1; ProcGlobal->bgwriterLatch = NULL; + ProcGlobal->walwriterLatch = NULL; /* * Create and initialize all the PGPROC structures we'll need (except for diff src/include/access/xlog.h index f8aecef..3c1a509 *** a/src/include/access/xlog.h --- b/src/include/access/xlog.h *************** extern CheckpointStatsData CheckpointSta *** 266,272 **** extern XLogRecPtr XLogInsert(RmgrId rmid, uint8 info, XLogRecData *rdata); extern void XLogFlush(XLogRecPtr RecPtr); ! extern void XLogBackgroundFlush(void); extern bool XLogNeedsFlush(XLogRecPtr RecPtr); extern int XLogFileInit(uint32 log, uint32 seg, bool *use_existent, bool use_lock); --- 266,272 ---- extern XLogRecPtr XLogInsert(RmgrId rmid, uint8 info, XLogRecData *rdata); extern void XLogFlush(XLogRecPtr RecPtr); ! extern bool XLogBackgroundFlush(void); extern bool XLogNeedsFlush(XLogRecPtr RecPtr); extern int XLogFileInit(uint32 log, uint32 seg, bool *use_existent, bool use_lock); *************** extern TimeLineID GetRecoveryTargetTLI(v *** 317,323 **** extern bool CheckPromoteSignal(void); extern void WakeupRecovery(void); - extern Latch *WALWriterLatch(void); /* * Starting/stopping a base backup --- 317,322 ---- diff src/include/storage/latch.h index f97fedf..12f500a *** a/src/include/storage/latch.h --- b/src/include/storage/latch.h *************** *** 25,31 **** * and must be initialized at postmaster startup by InitSharedLatch. Before * a shared latch can be waited on, it must be associated with a process * with OwnLatch. Only the process owning the latch can wait on it, but any ! * process can set it. * * There are three basic operations on a latch: * --- 25,37 ---- * and must be initialized at postmaster startup by InitSharedLatch. Before * a shared latch can be waited on, it must be associated with a process * with OwnLatch. Only the process owning the latch can wait on it, but any ! * process can set it. Note that the use of the process latch (a field in ! * PGPROC) is generally preferred over an ad-hoc latch for auxiliary processes. ! * This is because generic signal handlers will call SetLatch on the process ! * latch only. Signals have the potential to invalidate the latch timeout on ! * certain platforms, resulting in a denial-of-service, so even if the process ! * latch cannot be used it is important to verify that all signal handlers ! * within the process call SetLatch(). * * There are three basic operations on a latch: * diff src/include/storage/proc.h index a66c484..478577f *** a/src/include/storage/proc.h --- b/src/include/storage/proc.h *************** typedef struct PROC_HDR *** 190,195 **** --- 190,197 ---- PGPROC *autovacFreeProcs; /* BGWriter process latch */ Latch *bgwriterLatch; + /* WALWriter process latch */ + Latch *walwriterLatch; /* Current shared estimate of appropriate spins_per_delay value */ int spins_per_delay; /* The proc of the Startup process, since not in ProcArray */