Fix management of pendingOpsTable in auxiliary processes.

mdinit() was misusing IsBootstrapProcessingMode() to decide whether to
create an fsync pending-operations table in the current process.  This led
to creating a table not only in the startup and checkpointer processes as
intended, but also in the bgwriter process, not to mention other auxiliary
processes such as walwriter and walreceiver.  Creation of the table in the
bgwriter is fatal, because it absorbs fsync requests that should have gone
to the checkpointer; instead they just sit in bgwriter local memory and are
never acted on.  So writes performed by the bgwriter were not being fsync'd
which could result in data loss after an OS crash.  I think there is no
live bug with respect to walwriter and walreceiver because those never
perform any writes of shared buffers; but the potential is there for
future breakage in those processes too.

To fix, make AuxiliaryProcessMain() export the current process's
AuxProcType as a global variable, and then make mdinit() test directly for
the types of aux process that should have a pendingOpsTable.  Having done
that, we might as well also get rid of the random bool flags such as
am_walreceiver that some of the aux processes had grown.  (Note that we
could not have fixed the bug by examining those variables in mdinit(),
because it's called from BaseInit() which is run by AuxiliaryProcessMain()
before entering any of the process-type-specific code.)

Back-patch to 9.2, where the problem was introduced by the split-up of
bgwriter and checkpointer processes.  The bogus pendingOpsTable exists
in walwriter and walreceiver processes in earlier branches, but absent
any evidence that it causes actual problems there, I'll leave the older
branches alone.
This commit is contained in:
Tom Lane 2012-07-18 15:28:10 -04:00
parent 3855968f32
commit 4a9c30a8a1
11 changed files with 62 additions and 57 deletions

View File

@ -8946,7 +8946,7 @@ get_sync_bit(int method)
* after its written. Also, walreceiver performs unaligned writes, which
* don't work with O_DIRECT, so it is required for correctness too.
*/
if (!XLogIsNeeded() && !am_walreceiver)
if (!XLogIsNeeded() && !AmWalReceiverProcess())
o_direct_flag = PG_O_DIRECT;
switch (method)

View File

@ -63,6 +63,8 @@ static void cleanup(void);
* ----------------
*/
AuxProcType MyAuxProcType = NotAnAuxProcess; /* declared in miscadmin.h */
Relation boot_reldesc; /* current relation descriptor */
Form_pg_attribute attrtypes[MAXATTR]; /* points to attribute info */
@ -187,7 +189,6 @@ AuxiliaryProcessMain(int argc, char *argv[])
{
char *progname = argv[0];
int flag;
AuxProcType auxType = CheckerProcess;
char *userDoption = NULL;
/*
@ -228,6 +229,9 @@ AuxiliaryProcessMain(int argc, char *argv[])
argc--;
}
/* If no -x argument, we are a CheckerProcess */
MyAuxProcType = CheckerProcess;
while ((flag = getopt(argc, argv, "B:c:d:D:Fr:x:-:")) != -1)
{
switch (flag)
@ -258,7 +262,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
strlcpy(OutputFileName, optarg, MAXPGPATH);
break;
case 'x':
auxType = atoi(optarg);
MyAuxProcType = atoi(optarg);
break;
case 'c':
case '-':
@ -308,7 +312,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
{
const char *statmsg;
switch (auxType)
switch (MyAuxProcType)
{
case StartupProcess:
statmsg = "startup process";
@ -374,15 +378,15 @@ AuxiliaryProcessMain(int argc, char *argv[])
/*
* Assign the ProcSignalSlot for an auxiliary process. Since it
* doesn't have a BackendId, the slot is statically allocated based on
* the auxiliary process type (auxType). Backends use slots indexed
* in the range from 1 to MaxBackends (inclusive), so we use
* the auxiliary process type (MyAuxProcType). Backends use slots
* indexed in the range from 1 to MaxBackends (inclusive), so we use
* MaxBackends + AuxProcType + 1 as the index of the slot for an
* auxiliary process.
*
* This will need rethinking if we ever want more than one of a
* particular auxiliary process type.
*/
ProcSignalInit(MaxBackends + auxType + 1);
ProcSignalInit(MaxBackends + MyAuxProcType + 1);
/* finish setting up bufmgr.c */
InitBufferPoolBackend();
@ -396,7 +400,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
*/
SetProcessingMode(NormalProcessing);
switch (auxType)
switch (MyAuxProcType)
{
case CheckerProcess:
/* don't set signals, they're useless here */
@ -436,7 +440,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
proc_exit(1); /* should never return */
default:
elog(PANIC, "unrecognized process type: %d", auxType);
elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
proc_exit(1);
}
}

View File

@ -74,11 +74,6 @@ int BgWriterDelay = 200;
static volatile sig_atomic_t got_SIGHUP = false;
static volatile sig_atomic_t shutdown_requested = false;
/*
* Private state
*/
static bool am_bg_writer = false;
/* Signal handlers */
static void bg_quickdie(SIGNAL_ARGS);
@ -90,8 +85,8 @@ static void bgwriter_sigusr1_handler(SIGNAL_ARGS);
/*
* Main entry point for bgwriter process
*
* This is invoked from BootstrapMain, which has already created the basic
* execution environment, but not enabled signals yet.
* This is invoked from AuxiliaryProcessMain, which has already created the
* basic execution environment, but not enabled signals yet.
*/
void
BackgroundWriterMain(void)
@ -100,8 +95,6 @@ BackgroundWriterMain(void)
MemoryContext bgwriter_context;
bool prev_hibernate;
am_bg_writer = true;
/*
* If possible, make this process a group leader, so that the postmaster
* can signal any child processes too. (bgwriter probably never has any

View File

@ -153,8 +153,6 @@ static volatile sig_atomic_t shutdown_requested = false;
/*
* Private state
*/
static bool am_checkpointer = false;
static bool ckpt_active = false;
/* these values are valid when ckpt_active is true: */
@ -185,8 +183,8 @@ static void ReqShutdownHandler(SIGNAL_ARGS);
/*
* Main entry point for checkpointer process
*
* This is invoked from BootstrapMain, which has already created the basic
* execution environment, but not enabled signals yet.
* This is invoked from AuxiliaryProcessMain, which has already created the
* basic execution environment, but not enabled signals yet.
*/
void
CheckpointerMain(void)
@ -195,7 +193,6 @@ CheckpointerMain(void)
MemoryContext checkpointer_context;
CheckpointerShmem->checkpointer_pid = MyProcPid;
am_checkpointer = true;
/*
* If possible, make this process a group leader, so that the postmaster
@ -685,7 +682,7 @@ CheckpointWriteDelay(int flags, double progress)
static int absorb_counter = WRITES_PER_ABSORB;
/* Do nothing if checkpoint is being executed by non-checkpointer process */
if (!am_checkpointer)
if (!AmCheckpointerProcess())
return;
/*
@ -1129,7 +1126,7 @@ ForwardFsyncRequest(RelFileNode rnode, ForkNumber forknum, BlockNumber segno)
if (!IsUnderPostmaster)
return false; /* probably shouldn't even get here */
if (am_checkpointer)
if (AmCheckpointerProcess())
elog(ERROR, "ForwardFsyncRequest must not be called in checkpointer");
LWLockAcquire(CheckpointerCommLock, LW_EXCLUSIVE);
@ -1306,7 +1303,7 @@ AbsorbFsyncRequests(void)
CheckpointerRequest *request;
int n;
if (!am_checkpointer)
if (!AmCheckpointerProcess())
return;
/*

View File

@ -89,8 +89,8 @@ static void walwriter_sigusr1_handler(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.
* This is invoked from AuxiliaryProcessMain, which has already created the
* basic execution environment, but not enabled signals yet.
*/
void
WalWriterMain(void)

View File

@ -52,10 +52,8 @@
#include "utils/resowner.h"
#include "utils/timestamp.h"
/* Global variable to indicate if this process is a walreceiver process */
bool am_walreceiver;
/* GUC variable */
/* GUC variables */
int wal_receiver_status_interval;
bool hot_standby_feedback;
@ -176,8 +174,6 @@ WalReceiverMain(void)
/* use volatile pointer to prevent code rearrangement */
volatile WalRcvData *walrcv = WalRcv;
am_walreceiver = true;
/*
* WalRcv should be set up already (if we are a backend, we inherit this
* by fork() or EXEC_BACKEND mechanism from the postmaster).

View File

@ -17,7 +17,6 @@
#include <signal.h>
#include <unistd.h>
#include "bootstrap/bootstrap.h"
#include "commands/async.h"
#include "miscadmin.h"
#include "storage/latch.h"

View File

@ -196,11 +196,10 @@ mdinit(void)
/*
* Create pending-operations hashtable if we need it. Currently, we need
* it if we are standalone (not under a postmaster) OR if we are a
* bootstrap-mode subprocess of a postmaster (that is, a startup or
* checkpointer process).
* it if we are standalone (not under a postmaster) or if we are a startup
* or checkpointer auxiliary process.
*/
if (!IsUnderPostmaster || IsBootstrapProcessingMode())
if (!IsUnderPostmaster || AmStartupProcess() || AmCheckpointerProcess())
{
HASHCTL hash_ctl;

View File

@ -16,18 +16,6 @@
#include "nodes/execnodes.h"
typedef enum
{
CheckerProcess,
BootstrapProcess,
StartupProcess,
BgWriterProcess,
CheckpointerProcess,
WalWriterProcess,
WalReceiverProcess,
NUM_AUXPROCTYPES /* Must be last! */
} AuxProcType;
/*
* MAXATTR is the maximum number of attributes in a relation supported

View File

@ -328,8 +328,8 @@ extern bool superuser_arg(Oid roleid); /* given user is superuser */
* initialization is complete. Some code behaves differently when executed
* in this mode to enable system bootstrapping.
*
* If a POSTGRES binary is in normal mode, then all code may be executed
* normally.
* If a POSTGRES backend process is in normal mode, then all code may be
* executed normally.
*/
typedef enum ProcessingMode
@ -341,9 +341,11 @@ typedef enum ProcessingMode
extern ProcessingMode Mode;
#define IsBootstrapProcessingMode() ((bool)(Mode == BootstrapProcessing))
#define IsInitProcessingMode() ((bool)(Mode == InitProcessing))
#define IsNormalProcessingMode() ((bool)(Mode == NormalProcessing))
#define IsBootstrapProcessingMode() (Mode == BootstrapProcessing)
#define IsInitProcessingMode() (Mode == InitProcessing)
#define IsNormalProcessingMode() (Mode == NormalProcessing)
#define GetProcessingMode() Mode
#define SetProcessingMode(mode) \
do { \
@ -353,7 +355,35 @@ extern ProcessingMode Mode;
Mode = (mode); \
} while(0)
#define GetProcessingMode() Mode
/*
* Auxiliary-process type identifiers. These used to be in bootstrap.h
* but it seems saner to have them here, with the ProcessingMode stuff.
* The MyAuxProcType global is defined and set in bootstrap.c.
*/
typedef enum
{
NotAnAuxProcess = -1,
CheckerProcess = 0,
BootstrapProcess,
StartupProcess,
BgWriterProcess,
CheckpointerProcess,
WalWriterProcess,
WalReceiverProcess,
NUM_AUXPROCTYPES /* Must be last! */
} AuxProcType;
extern AuxProcType MyAuxProcType;
#define AmBootstrapProcess() (MyAuxProcType == BootstrapProcess)
#define AmStartupProcess() (MyAuxProcType == StartupProcess)
#define AmBackgroundWriterProcess() (MyAuxProcType == BgWriterProcess)
#define AmCheckpointerProcess() (MyAuxProcType == CheckpointerProcess)
#define AmWalWriterProcess() (MyAuxProcType == WalWriterProcess)
#define AmWalReceiverProcess() (MyAuxProcType == WalReceiverProcess)
/*****************************************************************************

View File

@ -17,7 +17,6 @@
#include "storage/spin.h"
#include "pgtime.h"
extern bool am_walreceiver;
extern int wal_receiver_status_interval;
extern bool hot_standby_feedback;