diff src/backend/access/transam/xlog.c index b584cb0..20e7b6c *** 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 ---- *************** XLogSetAsyncXactLSN(XLogRecPtr asyncXact *** 1935,1941 **** /* * Nudge the WALWriter if we have a full page of WAL to write. */ ! SetLatch(&XLogCtl->WALWriterLatch); } /* --- 1930,1937 ---- /* * 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 */ { --- 2169,2187 ---- * 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 --- 2223,2229 ---- XLogFileClose(); } } ! return false; } #ifdef WAL_DEBUG *************** XLogBackgroundFlush(void) *** 2247,2256 **** --- 2246,2258 ---- 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 --- 5103,5108 ---- *************** WakeupRecovery(void) *** 10478,10489 **** { SetLatch(&XLogCtl->recoveryWakeupLatch); } - - /* - * Manage the WALWriterLatch - */ - Latch * - WALWriterLatch(void) - { - return &XLogCtl->WALWriterLatch; - } --- 10479,10481 ---- diff src/backend/postmaster/checkpointer.c index 2329b1a..806de44 *** a/src/backend/postmaster/checkpointer.c --- b/src/backend/postmaster/checkpointer.c *************** *** 51,56 **** --- 51,57 ---- #include "storage/ipc.h" #include "storage/lwlock.h" #include "storage/pmsignal.h" + #include "storage/proc.h" #include "storage/shmem.h" #include "storage/smgr.h" #include "storage/spin.h" *************** int CheckPointWarning = 30; *** 144,149 **** --- 145,155 ---- double CheckPointCompletionTarget = 0.5; /* + * Number of seconds to sleep before checking for work. + */ + #define HIBERNATE_MS 15000 + + /* * Flags set by interrupt handlers for later service in the main loop. */ static volatile sig_atomic_t got_SIGHUP = false; *************** static void UpdateSharedMemoryConfig(voi *** 178,183 **** --- 184,190 ---- static void chkpt_quickdie(SIGNAL_ARGS); static void ChkptSigHupHandler(SIGNAL_ARGS); static void ReqCheckpointHandler(SIGNAL_ARGS); + static void chkpt_sigusr1_handler(SIGNAL_ARGS); static void ReqShutdownHandler(SIGNAL_ARGS); *************** CheckpointerMain(void) *** 224,230 **** pqsignal(SIGQUIT, chkpt_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, SIG_IGN); /* reserve for ProcSignal */ pqsignal(SIGUSR2, ReqShutdownHandler); /* request shutdown */ /* --- 231,237 ---- pqsignal(SIGQUIT, chkpt_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, chkpt_sigusr1_handler); pqsignal(SIGUSR2, ReqShutdownHandler); /* request shutdown */ /* *************** CheckpointerMain(void) *** 369,374 **** --- 376,383 ---- pg_time_t now; int elapsed_secs; + ResetLatch(&MyProc->procLatch); + /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. *************** CheckpointerMain(void) *** 548,562 **** */ pgstat_send_bgwriter(); - /* - * Nap for a while and then loop again. Later patches will replace - * this with a latch loop. Keep it simple now for clarity. - * Relatively long sleep because the bgwriter does cleanup now. - */ - pg_usleep(500000L); - /* Check for archive_timeout and switch xlog files if necessary. */ CheckArchiveTimeout(); } } --- 557,568 ---- */ pgstat_send_bgwriter(); /* Check for archive_timeout and switch xlog files if necessary. */ CheckArchiveTimeout(); + + (void) WaitLatch(&MyProc->procLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, + HIBERNATE_MS); } } *************** chkpt_quickdie(SIGNAL_ARGS) *** 814,834 **** --- 820,875 ---- static void ChkptSigHupHandler(SIGNAL_ARGS) { + int save_errno = errno; + got_SIGHUP = true; + if (MyProc) + SetLatch(&MyProc->procLatch); + + errno = save_errno; } /* SIGINT: set flag to run a normal checkpoint right away */ static void ReqCheckpointHandler(SIGNAL_ARGS) { + int save_errno = errno; + checkpoint_requested = true; + if (MyProc) + SetLatch(&MyProc->procLatch); + + errno = save_errno; + } + + /* + * SIGUSR1: used for latch wakeups. + * + * ReqCheckpointHandler() is actually responsible for servicing checkpoint + * requests. This is including for consistency with other auxiliary processes + * only. + */ + static void + chkpt_sigusr1_handler(SIGNAL_ARGS) + { + int save_errno = errno; + + latch_sigusr1_handler(); + + errno = save_errno; } /* SIGUSR2: set flag to run a shutdown checkpoint and exit */ static void ReqShutdownHandler(SIGNAL_ARGS) { + int save_errno = errno; + shutdown_requested = true; + if (MyProc) + SetLatch(&MyProc->procLatch); + + errno = save_errno; } diff src/backend/postmaster/walwriter.c index 08ef946..6721a97 *** 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 full hibernation, and time + * to sleep between walwriter rounds when hibernating. + */ + #define LOOPS_UNTIL_HIBERNATE 50 + #define HIBERNATE_MS 5000 + + /* * 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 wal_writer_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..419644a *** 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,38 ---- * 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 shared latch for auxiliary ! * processes. This is because generic signal handlers will call SetLatch on ! * the process latch only, so using any latch other than the process latch ! * effectively precludes ever registering a generic handler. Since signals have ! * the potential to invalidate the latch timeout on certain platforms, ! * resulting in a denial-of-service, it is important to verify that all signal ! * handlers within all WaitLatch() calling processes call SetLatch(). * * There are three basic operations on a latch: * diff src/include/storage/proc.h index 987bc08..46755e5 *** 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 */