*** a/doc/src/sgml/high-availability.sgml --- b/doc/src/sgml/high-availability.sgml *************** *** 959,964 **** synchronous_replication = on --- 959,970 ---- + When a smart shutdown is requested, new replication connections are + allowed in order to transfer all outstanding WAL records to standby + servers and wake up backends waiting for synchronous replication. + + + Users will stop waiting if a fast shutdown is requested, though the server does not fully shutdown until all outstanding WAL records are transferred to standby servers. *** a/doc/src/sgml/runtime.sgml --- b/doc/src/sgml/runtime.sgml *************** *** 1386,1392 **** echo -17 > /proc/self/oom_adj until online backup mode is no longer active. While backup mode is active, new connections will still be allowed, but only to superusers (this exception allows a superuser to connect to terminate ! online backup mode). If the server is in recovery when a smart shutdown is requested, recovery and streaming replication will be stopped only after all regular sessions have terminated. --- 1386,1396 ---- until online backup mode is no longer active. While backup mode is active, new connections will still be allowed, but only to superusers (this exception allows a superuser to connect to terminate ! online backup mode). While regular sessions are open, new replication ! connections will still be allowed (this exception allows WAL sender ! process to send all outstanding WAL records to standby servers and ! wake up backends waiting for synchronous replication). ! If the server is in recovery when a smart shutdown is requested, recovery and streaming replication will be stopped only after all regular sessions have terminated. *** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** *** 248,254 **** static bool RecoveryError = false; /* T if WAL recovery failed */ * * 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.) * 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 --- 248,255 ---- * * 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_AND_SYNCREP state, but only for ! * superusers and walsenders.) * 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 *************** *** 276,282 **** 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_WAIT_BACKENDS, /* waiting for live backends to exit */ PM_SHUTDOWN, /* waiting for bgwriter to do shutdown ckpt */ --- 277,284 ---- PM_RECOVERY, /* in archive recovery mode */ PM_HOT_STANDBY, /* in hot standby mode */ PM_RUN, /* normal "database is alive" state */ ! PM_WAIT_BACKUP_AND_SYNCREP, /* waiting for online backup mode and regular ! * backends (waiting for sync rep) to end */ PM_WAIT_READONLY, /* waiting for read only backends to exit */ PM_WAIT_BACKENDS, /* waiting for live backends to exit */ PM_SHUTDOWN, /* waiting for bgwriter to do shutdown ckpt */ *************** *** 1850,1856 **** retry1: (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already"))); break; ! case CAC_WAITBACKUP: /* OK for now, will check in InitPostgres */ break; case CAC_OK: --- 1852,1858 ---- (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already"))); break; ! case CAC_WAIT_BACKUP_AND_SYNCREP: /* OK for now, will check in InitPostgres */ break; case CAC_OK: *************** *** 1934,1949 **** canAcceptConnections(void) * Can't start backends when in startup/shutdown/inconsistent recovery * state. * ! * 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_WAIT_BACKUP) ! result = CAC_WAITBACKUP; /* allow superusers only */ else if (Shutdown > NoShutdown) return CAC_SHUTDOWN; /* shutdown is pending */ else if (!FatalError && --- 1936,1952 ---- * Can't start backends when in startup/shutdown/inconsistent recovery * state. * ! * In PM_WAIT_BACKUP_AND_SYNCREP state only superusers and standby servers ! * can connect (this must be allowed so that a superuser can end online ! * backup mode and walsender can wake up backends waiting for sync rep); ! * we return CAC_WAIT_BACKUP_AND_SYNCREP code to indicate that this must ! * be checked later. Note that neither CAC_OK nor CAC_WAIT_BACKUP_AND_SYNCREP ! * can safely be returned until we have checked for too many children. */ if (pmState != PM_RUN) { ! if (pmState == PM_WAIT_BACKUP_AND_SYNCREP) ! result = CAC_WAIT_BACKUP_AND_SYNCREP; /* allow superusers and walsenders only */ else if (Shutdown > NoShutdown) return CAC_SHUTDOWN; /* shutdown is pending */ else if (!FatalError && *************** *** 2214,2220 **** pmdie(SIGNAL_ARGS) * and walreceiver processes. */ pmState = (pmState == PM_RUN) ? ! PM_WAIT_BACKUP : PM_WAIT_READONLY; } /* --- 2217,2223 ---- * and walreceiver processes. */ pmState = (pmState == PM_RUN) ? ! PM_WAIT_BACKUP_AND_SYNCREP : PM_WAIT_READONLY; } /* *************** *** 2249,2255 **** pmdie(SIGNAL_ARGS) pmState = PM_WAIT_BACKENDS; } else if (pmState == PM_RUN || ! pmState == PM_WAIT_BACKUP || pmState == PM_WAIT_READONLY || pmState == PM_WAIT_BACKENDS || pmState == PM_HOT_STANDBY) --- 2252,2258 ---- pmState = PM_WAIT_BACKENDS; } else if (pmState == PM_RUN || ! pmState == PM_WAIT_BACKUP_AND_SYNCREP || pmState == PM_WAIT_READONLY || pmState == PM_WAIT_BACKENDS || pmState == PM_HOT_STANDBY) *************** *** 2828,2834 **** 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_SHUTDOWN) pmState = PM_WAIT_BACKENDS; --- 2831,2837 ---- if (pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_RUN || ! pmState == PM_WAIT_BACKUP_AND_SYNCREP || pmState == PM_WAIT_READONLY || pmState == PM_SHUTDOWN) pmState = PM_WAIT_BACKENDS; *************** *** 2896,2907 **** LogChildExit(int lev, const char *procname, int pid, int exitstatus) static void PostmasterStateMachine(void) { ! if (pmState == PM_WAIT_BACKUP) { /* ! * PM_WAIT_BACKUP state ends when online backup mode is not active. */ ! if (!BackupInProgress()) pmState = PM_WAIT_BACKENDS; } --- 2899,2911 ---- static void PostmasterStateMachine(void) { ! if (pmState == PM_WAIT_BACKUP_AND_SYNCREP) { /* ! * PM_WAIT_BACKUP_AND_SYNCREP state ends when online backup mode is ! * not active and there is no regular backend waiting for sync rep. */ ! if (!BackupInProgress() && CountChildren(BACKEND_TYPE_NORMAL) == 0) pmState = PM_WAIT_BACKENDS; } *************** *** 3233,3239 **** BackendStartup(Port *port) /* Pass down canAcceptConnections state */ port->canAcceptConnections = canAcceptConnections(); bn->dead_end = (port->canAcceptConnections != CAC_OK && ! port->canAcceptConnections != CAC_WAITBACKUP); /* * Unless it's a dead_end child, assign it a child slot number --- 3237,3243 ---- /* Pass down canAcceptConnections state */ port->canAcceptConnections = canAcceptConnections(); bn->dead_end = (port->canAcceptConnections != CAC_OK && ! port->canAcceptConnections != CAC_WAIT_BACKUP_AND_SYNCREP); /* * Unless it's a dead_end child, assign it a child slot number *** a/src/backend/utils/init/postinit.c --- b/src/backend/utils/init/postinit.c *************** *** 608,628 **** InitPostgres(const char *in_dbname, Oid dboid, const char *username, } /* ! * If we're trying to shut down, only superusers can connect, and new ! * replication connections are not allowed. */ ! if ((!am_superuser || am_walsender) && MyProcPort != NULL && ! MyProcPort->canAcceptConnections == CAC_WAITBACKUP) { ! if (am_walsender) ! ereport(FATAL, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("new replication connections are not allowed during database shutdown"))); ! else ! ereport(FATAL, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to connect during database shutdown"))); } /* --- 608,624 ---- } /* ! * If we're trying to shut down, only superusers and standby servers ! * can connect. */ ! if (!am_superuser && ! !am_walsender && MyProcPort != NULL && ! MyProcPort->canAcceptConnections == CAC_WAIT_BACKUP_AND_SYNCREP) { ! ereport(FATAL, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser or standby server to connect during database shutdown"))); } /* *** a/src/include/libpq/libpq-be.h --- b/src/include/libpq/libpq-be.h *************** *** 73,79 **** typedef struct typedef enum CAC_state { CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY, ! CAC_WAITBACKUP } CAC_state; --- 73,79 ---- typedef enum CAC_state { CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY, ! CAC_WAIT_BACKUP_AND_SYNCREP } CAC_state;