mirror of https://github.com/postgres/postgres
and fsync WAL at convenient intervals. For the moment it just tries to offload this work from backends, but soon it will be responsible for guaranteeing a maximum delay before asynchronously-committed transactions will be flushed to disk. This is a portion of Simon Riggs' async-commit patch, committed to CVS separately because a background WAL writer seems like it might be a good idea independently of the async-commit feature. I rebased walwriter.c on bgwriter.c because it seemed like a more appropriate way of handling signals; while the startup/shutdown logic in postmaster.c is more like autovac because we want walwriter to quit before we start the shutdown checkpoint.REL8_3_STABLE
parent
53d2951be7
commit
ad4295728e
@ -0,0 +1,311 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* walwriter.c |
||||
* |
||||
* The WAL writer background process is new as of Postgres 8.3. It attempts |
||||
* to keep regular backends from having to write out (and fsync) WAL pages. |
||||
* Also, it guarantees that transaction commit records that weren't synced |
||||
* to disk immediately upon commit (ie, were "asynchronously committed") |
||||
* will reach disk within a knowable time --- which, as it happens, is at |
||||
* most three times the wal_writer_delay cycle time. |
||||
* |
||||
* Note that as with the bgwriter for shared buffers, regular backends are |
||||
* still empowered to issue WAL writes and fsyncs when the walwriter doesn't |
||||
* keep up. |
||||
* |
||||
* Because the walwriter's cycle is directly linked to the maximum delay |
||||
* before async-commit transactions are guaranteed committed, it's probably |
||||
* unwise to load additional functionality onto it. For instance, if you've |
||||
* got a yen to create xlog segments further in advance, that'd be better done |
||||
* in bgwriter than in walwriter. |
||||
* |
||||
* The walwriter is started by the postmaster as soon as the startup subprocess |
||||
* finishes. It remains alive until the postmaster commands it to terminate. |
||||
* Normal termination is by SIGTERM, which instructs the walwriter to exit(0). |
||||
* Emergency termination is by SIGQUIT; like any backend, the walwriter will |
||||
* simply abort and exit on SIGQUIT. |
||||
* |
||||
* If the walwriter exits unexpectedly, the postmaster treats that the same |
||||
* as a backend crash: shared memory may be corrupted, so remaining backends |
||||
* should be killed by SIGQUIT and then a recovery cycle started. |
||||
* |
||||
* |
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/backend/postmaster/walwriter.c,v 1.1 2007/07/24 04:54:09 tgl Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include <signal.h> |
||||
#include <sys/time.h> |
||||
#include <time.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "access/xlog.h" |
||||
#include "libpq/pqsignal.h" |
||||
#include "miscadmin.h" |
||||
#include "postmaster/walwriter.h" |
||||
#include "storage/bufmgr.h" |
||||
#include "storage/ipc.h" |
||||
#include "storage/lwlock.h" |
||||
#include "storage/pmsignal.h" |
||||
#include "storage/smgr.h" |
||||
#include "utils/guc.h" |
||||
#include "utils/memutils.h" |
||||
#include "utils/resowner.h" |
||||
|
||||
|
||||
/*
|
||||
* GUC parameters |
||||
*/ |
||||
int WalWriterDelay = 200; |
||||
|
||||
/*
|
||||
* 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; |
||||
|
||||
/* Signal handlers */ |
||||
static void wal_quickdie(SIGNAL_ARGS); |
||||
static void WalSigHupHandler(SIGNAL_ARGS); |
||||
static void WalShutdownHandler(SIGNAL_ARGS); |
||||
|
||||
|
||||
/*
|
||||
* Main entry point for walwriter process |
||||
* |
||||
* This is invoked from BootstrapMain, which has already created the basic |
||||
* execution environment, but not enabled signals yet. |
||||
*/ |
||||
void |
||||
WalWriterMain(void) |
||||
{ |
||||
sigjmp_buf local_sigjmp_buf; |
||||
MemoryContext walwriter_context; |
||||
|
||||
/*
|
||||
* If possible, make this process a group leader, so that the postmaster |
||||
* can signal any child processes too. (walwriter probably never has |
||||
* any child processes, but for consistency we make all postmaster |
||||
* child processes do this.) |
||||
*/ |
||||
#ifdef HAVE_SETSID |
||||
if (setsid() < 0) |
||||
elog(FATAL, "setsid() failed: %m"); |
||||
#endif |
||||
|
||||
/*
|
||||
* Properly accept or ignore signals the postmaster might send us |
||||
* |
||||
* We have no particular use for SIGINT at the moment, but seems |
||||
* reasonable to treat like SIGTERM. |
||||
*/ |
||||
pqsignal(SIGHUP, WalSigHupHandler); /* set flag to read config file */ |
||||
pqsignal(SIGINT, WalShutdownHandler); /* request shutdown */ |
||||
pqsignal(SIGTERM, WalShutdownHandler); /* request shutdown */ |
||||
pqsignal(SIGQUIT, wal_quickdie); /* hard crash time */ |
||||
pqsignal(SIGALRM, SIG_IGN); |
||||
pqsignal(SIGPIPE, SIG_IGN); |
||||
pqsignal(SIGUSR1, SIG_IGN); /* reserve for sinval */ |
||||
pqsignal(SIGUSR2, SIG_IGN); /* not used */ |
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here |
||||
*/ |
||||
pqsignal(SIGCHLD, SIG_DFL); |
||||
pqsignal(SIGTTIN, SIG_DFL); |
||||
pqsignal(SIGTTOU, SIG_DFL); |
||||
pqsignal(SIGCONT, SIG_DFL); |
||||
pqsignal(SIGWINCH, SIG_DFL); |
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */ |
||||
#ifdef HAVE_SIGPROCMASK |
||||
sigdelset(&BlockSig, SIGQUIT); |
||||
#else |
||||
BlockSig &= ~(sigmask(SIGQUIT)); |
||||
#endif |
||||
|
||||
/*
|
||||
* Create a resource owner to keep track of our resources (not clear |
||||
* that we need this, but may as well have one). |
||||
*/ |
||||
CurrentResourceOwner = ResourceOwnerCreate(NULL, "Wal Writer"); |
||||
|
||||
/*
|
||||
* Create a memory context that we will do all our work in. We do this so |
||||
* that we can reset the context during error recovery and thereby avoid |
||||
* possible memory leaks. Formerly this code just ran in |
||||
* TopMemoryContext, but resetting that would be a really bad idea. |
||||
*/ |
||||
walwriter_context = AllocSetContextCreate(TopMemoryContext, |
||||
"Wal Writer", |
||||
ALLOCSET_DEFAULT_MINSIZE, |
||||
ALLOCSET_DEFAULT_INITSIZE, |
||||
ALLOCSET_DEFAULT_MAXSIZE); |
||||
MemoryContextSwitchTo(walwriter_context); |
||||
|
||||
/*
|
||||
* If an exception is encountered, processing resumes here. |
||||
* |
||||
* This code is heavily based on bgwriter.c, q.v. |
||||
*/ |
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) |
||||
{ |
||||
/* Since not using PG_TRY, must reset error stack by hand */ |
||||
error_context_stack = NULL; |
||||
|
||||
/* Prevent interrupts while cleaning up */ |
||||
HOLD_INTERRUPTS(); |
||||
|
||||
/* Report the error to the server log */ |
||||
EmitErrorReport(); |
||||
|
||||
/*
|
||||
* These operations are really just a minimal subset of |
||||
* AbortTransaction(). We don't have very many resources to worry |
||||
* about in walwriter, but we do have LWLocks, and perhaps buffers? |
||||
*/ |
||||
LWLockReleaseAll(); |
||||
AbortBufferIO(); |
||||
UnlockBuffers(); |
||||
/* buffer pins are released here: */ |
||||
ResourceOwnerRelease(CurrentResourceOwner, |
||||
RESOURCE_RELEASE_BEFORE_LOCKS, |
||||
false, true); |
||||
/* we needn't bother with the other ResourceOwnerRelease phases */ |
||||
AtEOXact_Buffers(false); |
||||
|
||||
/*
|
||||
* Now return to normal top-level context and clear ErrorContext for |
||||
* next time. |
||||
*/ |
||||
MemoryContextSwitchTo(walwriter_context); |
||||
FlushErrorState(); |
||||
|
||||
/* Flush any leaked data in the top-level context */ |
||||
MemoryContextResetAndDeleteChildren(walwriter_context); |
||||
|
||||
/* Now we can allow interrupts again */ |
||||
RESUME_INTERRUPTS(); |
||||
|
||||
/*
|
||||
* Sleep at least 1 second after any error. A write error is likely |
||||
* to be repeated, and we don't want to be filling the error logs as |
||||
* fast as we can. |
||||
*/ |
||||
pg_usleep(1000000L); |
||||
|
||||
/*
|
||||
* Close all open files after any error. This is helpful on Windows, |
||||
* where holding deleted files open causes various strange errors. |
||||
* It's not clear we need it elsewhere, but shouldn't hurt. |
||||
*/ |
||||
smgrcloseall(); |
||||
} |
||||
|
||||
/* We can now handle ereport(ERROR) */ |
||||
PG_exception_stack = &local_sigjmp_buf; |
||||
|
||||
/*
|
||||
* Unblock signals (they were blocked when the postmaster forked us) |
||||
*/ |
||||
PG_SETMASK(&UnBlockSig); |
||||
|
||||
/*
|
||||
* Loop forever |
||||
*/ |
||||
for (;;) |
||||
{ |
||||
long udelay; |
||||
|
||||
/*
|
||||
* Emergency bailout if postmaster has died. This is to avoid the |
||||
* necessity for manual cleanup of all postmaster children. |
||||
*/ |
||||
if (!PostmasterIsAlive(true)) |
||||
exit(1); |
||||
|
||||
/*
|
||||
* Process any requests or signals received recently. |
||||
*/ |
||||
if (got_SIGHUP) |
||||
{ |
||||
got_SIGHUP = false; |
||||
ProcessConfigFile(PGC_SIGHUP); |
||||
} |
||||
if (shutdown_requested) |
||||
{ |
||||
/* Normal exit from the walwriter is here */ |
||||
proc_exit(0); /* done */ |
||||
} |
||||
|
||||
/*
|
||||
* Do what we're here for... |
||||
*/ |
||||
XLogBackgroundFlush(); |
||||
|
||||
/*
|
||||
* Delay until time to do something more, but fall out of delay |
||||
* reasonably quickly if signaled. |
||||
*/ |
||||
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); |
||||
} |
||||
} |
||||
|
||||
|
||||
/* --------------------------------
|
||||
* signal handler routines |
||||
* -------------------------------- |
||||
*/ |
||||
|
||||
/*
|
||||
* wal_quickdie() occurs when signalled SIGQUIT by the postmaster. |
||||
* |
||||
* Some backend has bought the farm, |
||||
* so we need to stop what we're doing and exit. |
||||
*/ |
||||
static void |
||||
wal_quickdie(SIGNAL_ARGS) |
||||
{ |
||||
PG_SETMASK(&BlockSig); |
||||
|
||||
/*
|
||||
* DO NOT proc_exit() -- we're here because shared memory may be |
||||
* corrupted, so we don't want to try to clean up our transaction. Just |
||||
* nail the windows shut and get out of town. |
||||
* |
||||
* Note we do exit(2) not exit(0). This is to force the postmaster into a |
||||
* system reset cycle if some idiot DBA sends a manual SIGQUIT to a random |
||||
* backend. This is necessary precisely because we don't clean up our |
||||
* shared memory state. |
||||
*/ |
||||
exit(2); |
||||
} |
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */ |
||||
static void |
||||
WalSigHupHandler(SIGNAL_ARGS) |
||||
{ |
||||
got_SIGHUP = true; |
||||
} |
||||
|
||||
/* SIGTERM: set flag to exit normally */ |
||||
static void |
||||
WalShutdownHandler(SIGNAL_ARGS) |
||||
{ |
||||
shutdown_requested = true; |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* walwriter.h |
||||
* Exports from postmaster/walwriter.c. |
||||
* |
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group |
||||
* |
||||
* $PostgreSQL: pgsql/src/include/postmaster/walwriter.h,v 1.1 2007/07/24 04:54:09 tgl Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef _WALWRITER_H |
||||
#define _WALWRITER_H |
||||
|
||||
/* GUC options */ |
||||
extern int WalWriterDelay; |
||||
|
||||
extern void WalWriterMain(void); |
||||
|
||||
#endif /* _WALWRITER_H */ |
||||
Loading…
Reference in new issue