diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/postmaster/postmaster.c | 239 | ||||
-rw-r--r-- | src/backend/utils/init/postinit.c | 2 | ||||
-rw-r--r-- | src/include/libpq/libpq-be.h | 2 |
3 files changed, 124 insertions, 119 deletions
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b4d475bb0ba..260b8a6b524 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -148,8 +148,6 @@ #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 @@ -300,8 +298,7 @@ static bool FatalError = false; /* T if recovering from backend crash */ * 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 @@ -315,10 +312,10 @@ static bool FatalError = false; /* T if recovering from backend crash */ * * 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 { @@ -327,8 +324,7 @@ 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 */ @@ -340,6 +336,21 @@ typedef enum 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; @@ -2320,7 +2331,7 @@ retry1: (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: @@ -2440,32 +2451,37 @@ canAcceptConnections(int backend_type) * 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. * * We allow more connections here than we can have backends because some @@ -2790,34 +2806,22 @@ pmdie(SIGNAL_ARGS) 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; } /* @@ -2848,48 +2852,23 @@ pmdie(SIGNAL_ARGS) 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; @@ -2984,7 +2963,7 @@ reaper(SIGNAL_ARGS) 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 */ @@ -3048,6 +3027,7 @@ reaper(SIGNAL_ARGS) AbortStartTime = 0; ReachedNormalRunning = true; pmState = PM_RUN; + connsAllowed = ALLOW_ALL_CONNS; /* * Crank up the background tasks, if we didn't do that already @@ -3709,8 +3689,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) 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; @@ -3793,36 +3772,61 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus) 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. */ @@ -3840,7 +3844,7 @@ PostmasterStateMachine(void) * 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 && @@ -4181,7 +4185,7 @@ BackendStartup(Port *port) /* 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 @@ -5252,6 +5256,8 @@ sigusr1_handler(SIGNAL_ARGS) #endif pmState = PM_HOT_STANDBY; + connsAllowed = ALLOW_ALL_CONNS; + /* Some workers may be scheduled to start now */ StartWorkerNeeded = true; } @@ -5284,7 +5290,7 @@ sigusr1_handler(SIGNAL_ARGS) } if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) && - Shutdown == NoShutdown) + Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS) { /* * Start one iteration of the autovacuum daemon, even if autovacuuming @@ -5299,7 +5305,7 @@ sigusr1_handler(SIGNAL_ARGS) } 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(); @@ -5330,7 +5336,7 @@ sigusr1_handler(SIGNAL_ARGS) if (StartupPID != 0 && (pmState == PM_STARTUP || pmState == PM_RECOVERY || - pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) && + pmState == PM_HOT_STANDBY) && CheckPromoteSignal()) { /* Tell startup process to finish recovery */ @@ -5643,8 +5649,8 @@ MaybeStartWalReceiver(void) { 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) @@ -5897,8 +5903,7 @@ bgworker_should_start_now(BgWorkerStartTime start_time) 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: diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index f4247ea70d5..6cf0cef6f30 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -791,7 +791,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, */ if ((!am_superuser || am_walsender) && MyProcPort != NULL && - MyProcPort->canAcceptConnections == CAC_WAITBACKUP) + MyProcPort->canAcceptConnections == CAC_SUPERUSER) { if (am_walsender) ereport(FATAL, diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 179ebaa104b..0a23281ad59 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -71,7 +71,7 @@ typedef struct typedef enum CAC_state { CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY, - CAC_WAITBACKUP + CAC_SUPERUSER } CAC_state; |