static bool FatalError = false; /* T if recovering from backend crash */
static bool RecoveryError = false; /* T if recovery failed */
-/* State of WAL redo */
-#define NoRecovery 0
-#define RecoveryStarted 1
-#define RecoveryConsistent 2
-#define RecoveryCompleted 3
-
-static int RecoveryStatus = NoRecovery;
-
/*
* We use a simple state machine to control startup, shutdown, and
* crash recovery (which is rather like shutdown followed by startup).
* point, if we had the infrastructure to do that.
*
* When the WAL redo is finished, the startup process signals us the third
- * time, and we switch to PM_RUN state. The startup process can also skip the
- * recovery and consistent recovery phases altogether, as it will during
- * normal startup when there's no recovery to be done, for example.
+ * time, and exits. We don't process the 3d signal immediately but when we
+ * see the that the startup process has exited, we check that we have
+ * received the signal. If everything is OK, we then switch to PM_RUN state.
+ * The startup process can also skip the recovery and consistent recovery
+ * phases altogether, as it will during normal startup when there's no
+ * recovery to be done, for example.
*
* Normal child backends can only be launched when we are in PM_RUN state.
* (We also allow it in PM_WAIT_BACKUP state, but only for superusers.)
static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
-static void CheckRecoverySignals(void);
static void CleanupBackend(int pid, int exitstatus);
static void HandleChildCrash(int pid, int exitstatus, const char *procname);
static void LogChildExit(int lev, const char *procname,
ereport(LOG,
(errmsg("received smart shutdown request")));
- if (pmState == PM_RUN || pmState == PM_RECOVERY || pmState == PM_RECOVERY_CONSISTENT)
+ if (pmState == PM_RUN || pmState == PM_RECOVERY ||
+ pmState == PM_RECOVERY_CONSISTENT)
{
/* autovacuum workers are told to shut down immediately */
SignalAutovacWorkers(SIGTERM);
*/
if (pid == StartupPID)
{
+ bool recoveryCompleted;
+
StartupPID = 0;
/*
- * Check if we've received a signal from the startup process
- * first. This can change pmState. If the startup process sends
- * a signal and exits immediately after that, we might not have
- * processed the signal yet. We need to know if it completed
- * recovery before it exited.
+ * Check if the startup process completed recovery before exiting
*/
- CheckRecoverySignals();
+ if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_COMPLETED))
+ recoveryCompleted = true;
+ else
+ recoveryCompleted = false;
/*
* Unexpected exit of startup process (including FATAL exit)
* during PM_STARTUP is treated as catastrophic. There is no
- * other processes running yet.
+ * other processes running yet, so we can just exit.
*/
- if (pmState == PM_STARTUP)
+ if (pmState == PM_STARTUP && !recoveryCompleted)
{
LogChildExit(LOG, _("startup process"),
pid, exitstatus);
_("startup process"));
continue;
}
+ /*
+ * Startup process exited in response to a shutdown request (or
+ * it finished normally regardless of the shutdown request).
+ */
+ if (Shutdown > NoShutdown)
+ {
+ pmState = PM_WAIT_BACKENDS;
+ /* PostmasterStateMachine logic does the rest */
+ continue;
+ }
/*
* Startup process exited normally, but didn't finish recovery.
* This can happen if someone else than postmaster kills the
* startup process with SIGTERM. Treat it like a crash.
*/
- if (pmState == PM_RECOVERY || pmState == PM_RECOVERY_CONSISTENT)
+ if (!recoveryCompleted)
{
RecoveryError = true;
HandleChildCrash(pid, exitstatus,
_("startup process"));
continue;
}
+
+ /*
+ * Startup succeeded, commence normal operations
+ */
+ pmState = PM_RUN;
+
+ /*
+ * Load the flat authorization file into postmaster's cache. The
+ * startup process has recomputed this from the database contents,
+ * so we wait till it finishes before loading it.
+ */
+ load_role();
+
+ /*
+ * Crank up the background writer, if we didn't do that already
+ * when we entered consistent recovery phase. It doesn't matter
+ * if this fails, we'll just try again later.
+ */
+ if (BgWriterPID == 0)
+ BgWriterPID = StartBackgroundWriter();
+
+ /*
+ * Likewise, start other special children as needed. In a restart
+ * situation, some of them may be alive already.
+ */
+ if (WalWriterPID == 0)
+ WalWriterPID = StartWalWriter();
+ if (AutoVacuumingActive() && AutoVacPID == 0)
+ AutoVacPID = StartAutoVacLauncher();
+ if (XLogArchivingActive() && PgArchPID == 0)
+ PgArchPID = pgarch_start();
+ if (PgStatPID == 0)
+ PgStatPID = pgstat_start();
+
+ /* at this point we are really open for business */
+ ereport(LOG,
+ (errmsg("database system is ready to accept connections")));
}
/*
static void
PostmasterStateMachine(void)
{
- /* Startup states */
-
- if (pmState == PM_STARTUP && RecoveryStatus > NoRecovery)
- {
- /* WAL redo has started. We're out of reinitialization. */
- FatalError = false;
-
- /*
- * Go to shutdown mode if a shutdown request was pending.
- */
- if (Shutdown > NoShutdown)
- {
- pmState = PM_WAIT_BACKENDS;
- /* PostmasterStateMachine logic does the rest */
- }
- else
- {
- /*
- * Crank up the background writer. It doesn't matter if this
- * fails, we'll just try again later.
- */
- Assert(BgWriterPID == 0);
- BgWriterPID = StartBackgroundWriter();
-
- pmState = PM_RECOVERY;
- }
- }
- if (pmState == PM_RECOVERY && RecoveryStatus >= RecoveryConsistent)
- {
- /*
- * Go to shutdown mode if a shutdown request was pending.
- */
- if (Shutdown > NoShutdown)
- {
- pmState = PM_WAIT_BACKENDS;
- /* PostmasterStateMachine logic does the rest */
- }
- else
- {
- /*
- * Startup process has entered recovery. We consider that good
- * enough to reset FatalError.
- */
- pmState = PM_RECOVERY_CONSISTENT;
-
- /*
- * Load the flat authorization file into postmaster's cache. The
- * startup process won't have recomputed this from the database yet,
- * so we it may change following recovery.
- */
- load_role();
-
- /*
- * Likewise, start other special children as needed.
- */
- Assert(PgStatPID == 0);
- PgStatPID = pgstat_start();
-
- /* XXX at this point we could accept read-only connections */
- ereport(DEBUG1,
- (errmsg("database system is in consistent recovery mode")));
- }
- }
- if ((pmState == PM_RECOVERY ||
- pmState == PM_RECOVERY_CONSISTENT ||
- pmState == PM_STARTUP) &&
- RecoveryStatus == RecoveryCompleted)
- {
- /*
- * Startup succeeded.
- *
- * Go to shutdown mode if a shutdown request was pending.
- */
- if (Shutdown > NoShutdown)
- {
- pmState = PM_WAIT_BACKENDS;
- /* PostmasterStateMachine logic does the rest */
- }
- else
- {
- /*
- * Otherwise, commence normal operations.
- */
- pmState = PM_RUN;
-
- /*
- * Load the flat authorization file into postmaster's cache. The
- * startup process has recomputed this from the database contents,
- * so we wait till it finishes before loading it.
- */
- load_role();
-
- /*
- * Crank up the background writer, if we didn't do that already
- * when we entered consistent recovery phase. It doesn't matter
- * if this fails, we'll just try again later.
- */
- if (BgWriterPID == 0)
- BgWriterPID = StartBackgroundWriter();
-
- /*
- * Likewise, start other special children as needed. In a restart
- * situation, some of them may be alive already.
- */
- if (WalWriterPID == 0)
- WalWriterPID = StartWalWriter();
- if (AutoVacuumingActive() && AutoVacPID == 0)
- AutoVacPID = StartAutoVacLauncher();
- if (XLogArchivingActive() && PgArchPID == 0)
- PgArchPID = pgarch_start();
- if (PgStatPID == 0)
- PgStatPID = pgstat_start();
-
- /* at this point we are really open for business */
- ereport(LOG,
- (errmsg("database system is ready to accept connections")));
- }
- }
-
- /* Shutdown states */
-
if (pmState == PM_WAIT_BACKUP)
{
/*
shmem_exit(1);
reset_shared(PostPortNumber);
- RecoveryStatus = NoRecovery;
-
StartupPID = StartupDataBase();
Assert(StartupPID != 0);
pmState = PM_STARTUP;
}
/*
- * common code used in sigusr1_handler() and reaper() to handle
- * recovery-related signals from startup process
+ * sigusr1_handler - handle signal conditions from child processes
*/
static void
-CheckRecoverySignals(void)
+sigusr1_handler(SIGNAL_ARGS)
{
- bool changed = false;
+ int save_errno = errno;
- if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED))
- {
- Assert(pmState == PM_STARTUP);
+ PG_SETMASK(&BlockSig);
- RecoveryStatus = RecoveryStarted;
- changed = true;
- }
- if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_CONSISTENT))
+ /*
+ * RECOVERY_STARTED and RECOVERY_CONSISTENT signals are ignored in
+ * unexpected states. If the startup process quickly starts up, completes
+ * recovery, exits, we might process the death of the startup process
+ * first. We don't want to go back to recovery in that case.
+ */
+ if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED) &&
+ pmState == PM_STARTUP)
{
- RecoveryStatus = RecoveryConsistent;
- changed = true;
+ /* WAL redo has started. We're out of reinitialization. */
+ FatalError = false;
+
+ /*
+ * Crank up the background writer. It doesn't matter if this
+ * fails, we'll just try again later.
+ */
+ Assert(BgWriterPID == 0);
+ BgWriterPID = StartBackgroundWriter();
+
+ pmState = PM_RECOVERY;
}
- if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_COMPLETED))
+ if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_CONSISTENT) &&
+ pmState == PM_RECOVERY)
{
- RecoveryStatus = RecoveryCompleted;
- changed = true;
- }
-
- if (changed)
- PostmasterStateMachine();
-}
+ /*
+ * Load the flat authorization file into postmaster's cache. The
+ * startup process won't have recomputed this from the database yet,
+ * so we it may change following recovery.
+ */
+ load_role();
-/*
- * sigusr1_handler - handle signal conditions from child processes
- */
-static void
-sigusr1_handler(SIGNAL_ARGS)
-{
- int save_errno = errno;
+ /*
+ * Likewise, start other special children as needed.
+ */
+ Assert(PgStatPID == 0);
+ PgStatPID = pgstat_start();
- PG_SETMASK(&BlockSig);
+ /* XXX at this point we could accept read-only connections */
+ ereport(DEBUG1,
+ (errmsg("database system is in consistent recovery mode")));
- CheckRecoverySignals();
+ pmState = PM_RECOVERY_CONSISTENT;
+ }
if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
{