#define BACKEND_TYPE_BGWORKER 0x0008 /* bgworker process */
#define BACKEND_TYPE_ALL 0x000F /* OR of all the above */
-#define BACKEND_TYPE_WORKER (BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
-
/*
* List of active backends (or child processes anyway; we don't actually
* know whether a given child has become a backend or is still in the
* and we switch to PM_RUN state.
*
* Normal child backends can only be launched when we are in PM_RUN or
- * PM_HOT_STANDBY state. (We also allow launch of normal
- * child backends in PM_WAIT_BACKUP state, but only for superusers.)
+ * PM_HOT_STANDBY state. (connsAllowed can also restrict launching.)
* In other states we handle connection requests by launching "dead_end"
* child processes, which will simply send the client an error message and
* quit. (We track these in the BackendList so that we can know when they
*
* Notice that this state variable does not distinguish *why* we entered
* states later than PM_RUN --- Shutdown and FatalError must be consulted
- * to find that out. FatalError is never true in PM_RECOVERY_* or PM_RUN
- * states, nor in PM_SHUTDOWN states (because we don't enter those states
- * when trying to recover from a crash). It can be true in PM_STARTUP state,
- * because we don't clear it until we've successfully started WAL redo.
+ * to find that out. FatalError is never true in PM_RECOVERY, PM_HOT_STANDBY,
+ * or PM_RUN states, nor in PM_SHUTDOWN states (because we don't enter those
+ * states when trying to recover from a crash). It can be true in PM_STARTUP
+ * state, because we don't clear it until we've successfully started WAL redo.
*/
typedef enum
{
PM_RECOVERY, /* in archive recovery mode */
PM_HOT_STANDBY, /* in hot standby mode */
PM_RUN, /* normal "database is alive" state */
- PM_WAIT_BACKUP, /* waiting for online backup mode to end */
- PM_WAIT_READONLY, /* waiting for read only backends to exit */
+ PM_STOP_BACKENDS, /* need to stop remaining backends */
PM_WAIT_BACKENDS, /* waiting for live backends to exit */
PM_SHUTDOWN, /* waiting for checkpointer to do shutdown
* ckpt */
static PMState pmState = PM_INIT;
+/*
+ * While performing a "smart shutdown", we restrict new connections but stay
+ * in PM_RUN or PM_HOT_STANDBY state until all the client backends are gone.
+ * connsAllowed is a sub-state indicator showing the active restriction.
+ * It is of no interest unless pmState is PM_RUN or PM_HOT_STANDBY.
+ */
+typedef enum
+{
+ ALLOW_ALL_CONNS, /* normal not-shutting-down state */
+ ALLOW_SUPERUSER_CONNS, /* only superusers can connect */
+ ALLOW_NO_CONNS /* no new connections allowed, period */
+} ConnsAllowedState;
+
+static ConnsAllowedState connsAllowed = ALLOW_ALL_CONNS;
+
/* Start time of SIGKILL timeout during immediate shutdown or child crash */
/* Zero means timeout is not running */
static time_t AbortStartTime = 0;
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
errmsg("sorry, too many clients already")));
break;
- case CAC_WAITBACKUP:
+ case CAC_SUPERUSER:
/* OK for now, will check in InitPostgres */
break;
case CAC_OK:
* state. We treat autovac workers the same as user backends for this
* purpose. However, bgworkers are excluded from this test; we expect
* bgworker_should_start_now() decided whether the DB state allows them.
- *
- * In state PM_WAIT_BACKUP only superusers can connect (this must be
- * allowed so that a superuser can end online backup mode); we return
- * CAC_WAITBACKUP code to indicate that this must be checked later. Note
- * that neither CAC_OK nor CAC_WAITBACKUP can safely be returned until we
- * have checked for too many children.
*/
- if (pmState != PM_RUN &&
+ if (pmState != PM_RUN && pmState != PM_HOT_STANDBY &&
backend_type != BACKEND_TYPE_BGWORKER)
{
- if (pmState == PM_WAIT_BACKUP)
- result = CAC_WAITBACKUP; /* allow superusers only */
- else if (Shutdown > NoShutdown)
+ if (Shutdown > NoShutdown)
return CAC_SHUTDOWN; /* shutdown is pending */
else if (!FatalError &&
(pmState == PM_STARTUP ||
pmState == PM_RECOVERY))
return CAC_STARTUP; /* normal startup */
- else if (!FatalError &&
- pmState == PM_HOT_STANDBY)
- result = CAC_OK; /* connection OK during hot standby */
else
return CAC_RECOVERY; /* else must be crash recovery */
}
+ /*
+ * "Smart shutdown" restrictions are applied only to normal connections,
+ * not to autovac workers or bgworkers. When only superusers can connect,
+ * we return CAC_SUPERUSER to indicate that superuserness must be checked
+ * later. Note that neither CAC_OK nor CAC_SUPERUSER can safely be
+ * returned until we have checked for too many children.
+ */
+ if (connsAllowed != ALLOW_ALL_CONNS &&
+ backend_type == BACKEND_TYPE_NORMAL)
+ {
+ if (connsAllowed == ALLOW_SUPERUSER_CONNS)
+ result = CAC_SUPERUSER; /* allow superusers only */
+ else
+ return CAC_SHUTDOWN; /* shutdown is pending */
+ }
+
/*
* Don't start too many children.
*
sd_notify(0, "STOPPING=1");
#endif
- if (pmState == PM_RUN || pmState == PM_RECOVERY ||
- pmState == PM_HOT_STANDBY || pmState == PM_STARTUP)
+ /*
+ * If we reached normal running, we have to wait for any online
+ * backup mode to end; otherwise go straight to waiting for client
+ * backends to exit. (The difference is that in the former state,
+ * we'll still let in new superuser clients, so that somebody can
+ * end the online backup mode.) If already in PM_STOP_BACKENDS or
+ * a later state, do not change it.
+ */
+ if (pmState == PM_RUN)
+ connsAllowed = ALLOW_SUPERUSER_CONNS;
+ else if (pmState == PM_HOT_STANDBY)
+ connsAllowed = ALLOW_NO_CONNS;
+ else if (pmState == PM_STARTUP || pmState == PM_RECOVERY)
{
- /* autovac workers are told to shut down immediately */
- /* and bgworkers too; does this need tweaking? */
- SignalSomeChildren(SIGTERM,
- BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER);
- /* and the autovac launcher too */
- if (AutoVacPID != 0)
- signal_child(AutoVacPID, SIGTERM);
- /* and the bgwriter too */
- if (BgWriterPID != 0)
- signal_child(BgWriterPID, SIGTERM);
- /* and the walwriter too */
- if (WalWriterPID != 0)
- signal_child(WalWriterPID, SIGTERM);
-
- /*
- * If we're in recovery, we can't kill the startup process
- * right away, because at present doing so does not release
- * its locks. We might want to change this in a future
- * release. For the time being, the PM_WAIT_READONLY state
- * indicates that we're waiting for the regular (read only)
- * backends to die off; once they do, we'll kill the startup
- * and walreceiver processes.
- */
- pmState = (pmState == PM_RUN) ?
- PM_WAIT_BACKUP : PM_WAIT_READONLY;
+ /* There should be no clients, so proceed to stop children */
+ pmState = PM_STOP_BACKENDS;
}
/*
sd_notify(0, "STOPPING=1");
#endif
- if (StartupPID != 0)
- signal_child(StartupPID, SIGTERM);
- if (BgWriterPID != 0)
- signal_child(BgWriterPID, SIGTERM);
- if (WalReceiverPID != 0)
- signal_child(WalReceiverPID, SIGTERM);
if (pmState == PM_STARTUP || pmState == PM_RECOVERY)
{
- SignalSomeChildren(SIGTERM, BACKEND_TYPE_BGWORKER);
-
- /*
- * Only startup, bgwriter, walreceiver, possibly bgworkers,
- * and/or checkpointer should be active in this state; we just
- * signaled the first four, and we don't want to kill
- * checkpointer yet.
- */
- pmState = PM_WAIT_BACKENDS;
+ /* Just shut down background processes silently */
+ pmState = PM_STOP_BACKENDS;
}
else if (pmState == PM_RUN ||
- pmState == PM_WAIT_BACKUP ||
- pmState == PM_WAIT_READONLY ||
- pmState == PM_WAIT_BACKENDS ||
pmState == PM_HOT_STANDBY)
{
+ /* Report that we're about to zap live client sessions */
ereport(LOG,
(errmsg("aborting any active transactions")));
- /* shut down all backends and workers */
- SignalSomeChildren(SIGTERM,
- BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC |
- BACKEND_TYPE_BGWORKER);
- /* and the autovac launcher too */
- if (AutoVacPID != 0)
- signal_child(AutoVacPID, SIGTERM);
- /* and the walwriter too */
- if (WalWriterPID != 0)
- signal_child(WalWriterPID, SIGTERM);
- pmState = PM_WAIT_BACKENDS;
+ pmState = PM_STOP_BACKENDS;
}
/*
- * Now wait for backends to exit. If there are none,
- * PostmasterStateMachine will take the next step.
+ * PostmasterStateMachine will issue any necessary signals, or
+ * take the next step if no child processes need to be killed.
*/
PostmasterStateMachine();
break;
ereport(LOG,
(errmsg("shutdown at recovery target")));
StartupStatus = STARTUP_NOT_RUNNING;
- Shutdown = SmartShutdown;
+ Shutdown = Max(Shutdown, SmartShutdown);
TerminateChildren(SIGTERM);
pmState = PM_WAIT_BACKENDS;
/* PostmasterStateMachine logic does the rest */
AbortStartTime = 0;
ReachedNormalRunning = true;
pmState = PM_RUN;
+ connsAllowed = ALLOW_ALL_CONNS;
/*
* Crank up the background tasks, if we didn't do that already
if (pmState == PM_RECOVERY ||
pmState == PM_HOT_STANDBY ||
pmState == PM_RUN ||
- pmState == PM_WAIT_BACKUP ||
- pmState == PM_WAIT_READONLY ||
+ pmState == PM_STOP_BACKENDS ||
pmState == PM_SHUTDOWN)
pmState = PM_WAIT_BACKENDS;
static void
PostmasterStateMachine(void)
{
- if (pmState == PM_WAIT_BACKUP)
+ /* If we're doing a smart shutdown, try to advance that state. */
+ if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
{
- /*
- * PM_WAIT_BACKUP state ends when online backup mode is not active.
- */
- if (!BackupInProgress())
- pmState = PM_WAIT_BACKENDS;
- }
+ if (connsAllowed == ALLOW_SUPERUSER_CONNS)
+ {
+ /*
+ * ALLOW_SUPERUSER_CONNS state ends as soon as online backup mode
+ * is not active.
+ */
+ if (!BackupInProgress())
+ connsAllowed = ALLOW_NO_CONNS;
+ }
- if (pmState == PM_WAIT_READONLY)
- {
- /*
- * PM_WAIT_READONLY state ends when we have no regular backends that
- * have been started during recovery. We kill the startup and
- * walreceiver processes and transition to PM_WAIT_BACKENDS. Ideally,
- * we might like to kill these processes first and then wait for
- * backends to die off, but that doesn't work at present because
- * killing the startup process doesn't release its locks.
- */
- if (CountChildren(BACKEND_TYPE_NORMAL) == 0)
+ if (connsAllowed == ALLOW_NO_CONNS)
{
- if (StartupPID != 0)
- signal_child(StartupPID, SIGTERM);
- if (WalReceiverPID != 0)
- signal_child(WalReceiverPID, SIGTERM);
- pmState = PM_WAIT_BACKENDS;
+ /*
+ * ALLOW_NO_CONNS state ends when we have no normal client
+ * backends running. Then we're ready to stop other children.
+ */
+ if (CountChildren(BACKEND_TYPE_NORMAL) == 0)
+ pmState = PM_STOP_BACKENDS;
}
}
+ /*
+ * If we're ready to do so, signal child processes to shut down. (This
+ * isn't a persistent state, but treating it as a distinct pmState allows
+ * us to share this code across multiple shutdown code paths.)
+ */
+ if (pmState == PM_STOP_BACKENDS)
+ {
+ /* Signal all backend children except walsenders */
+ SignalSomeChildren(SIGTERM,
+ BACKEND_TYPE_ALL - BACKEND_TYPE_WALSND);
+ /* and the autovac launcher too */
+ if (AutoVacPID != 0)
+ signal_child(AutoVacPID, SIGTERM);
+ /* and the bgwriter too */
+ if (BgWriterPID != 0)
+ signal_child(BgWriterPID, SIGTERM);
+ /* and the walwriter too */
+ if (WalWriterPID != 0)
+ signal_child(WalWriterPID, SIGTERM);
+ /* If we're in recovery, also stop startup and walreceiver procs */
+ if (StartupPID != 0)
+ signal_child(StartupPID, SIGTERM);
+ if (WalReceiverPID != 0)
+ signal_child(WalReceiverPID, SIGTERM);
+ /* checkpointer, archiver, stats, and syslogger may continue for now */
+
+ /* Now transition to PM_WAIT_BACKENDS state to wait for them to die */
+ pmState = PM_WAIT_BACKENDS;
+ }
+
/*
* If we are in a state-machine state that implies waiting for backends to
* exit, see if they're all gone, and change state if so.
* later after writing the checkpoint record, like the archiver
* process.
*/
- if (CountChildren(BACKEND_TYPE_NORMAL | BACKEND_TYPE_WORKER) == 0 &&
+ if (CountChildren(BACKEND_TYPE_ALL - BACKEND_TYPE_WALSND) == 0 &&
StartupPID == 0 &&
WalReceiverPID == 0 &&
BgWriterPID == 0 &&
/* Pass down canAcceptConnections state */
port->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
bn->dead_end = (port->canAcceptConnections != CAC_OK &&
- port->canAcceptConnections != CAC_WAITBACKUP);
+ port->canAcceptConnections != CAC_SUPERUSER);
/*
* Unless it's a dead_end child, assign it a child slot number
#endif
pmState = PM_HOT_STANDBY;
+ connsAllowed = ALLOW_ALL_CONNS;
+
/* Some workers may be scheduled to start now */
StartWorkerNeeded = true;
}
}
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) &&
- Shutdown == NoShutdown)
+ Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
{
/*
* Start one iteration of the autovacuum daemon, even if autovacuuming
}
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER) &&
- Shutdown == NoShutdown)
+ Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
{
/* The autovacuum launcher wants us to start a worker process. */
StartAutovacuumWorker();
if (StartupPID != 0 &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY ||
- pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
+ pmState == PM_HOT_STANDBY) &&
CheckPromoteSignal())
{
/*
{
if (WalReceiverPID == 0 &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY ||
- pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
- Shutdown == NoShutdown)
+ pmState == PM_HOT_STANDBY) &&
+ Shutdown <= SmartShutdown)
{
WalReceiverPID = StartWalReceiver();
if (WalReceiverPID != 0)
case PM_SHUTDOWN_2:
case PM_SHUTDOWN:
case PM_WAIT_BACKENDS:
- case PM_WAIT_READONLY:
- case PM_WAIT_BACKUP:
+ case PM_STOP_BACKENDS:
break;
case PM_RUN: