379 lines
9.2 KiB
C
379 lines
9.2 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* startup.c
|
|
*
|
|
* The Startup process initialises the server and performs any recovery
|
|
* actions that have been specified. Notice that there is no "main loop"
|
|
* since the Startup process ends as soon as initialisation is complete.
|
|
* (in standby mode, one can think of the replay loop as a main loop,
|
|
* though.)
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/postmaster/startup.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/xlog.h"
|
|
#include "access/xlogrecovery.h"
|
|
#include "access/xlogutils.h"
|
|
#include "libpq/pqsignal.h"
|
|
#include "miscadmin.h"
|
|
#include "postmaster/auxprocess.h"
|
|
#include "postmaster/startup.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/pmsignal.h"
|
|
#include "storage/procsignal.h"
|
|
#include "storage/standby.h"
|
|
#include "utils/guc.h"
|
|
#include "utils/memutils.h"
|
|
#include "utils/timeout.h"
|
|
|
|
|
|
#ifndef USE_POSTMASTER_DEATH_SIGNAL
|
|
/*
|
|
* On systems that need to make a system call to find out if the postmaster has
|
|
* gone away, we'll do so only every Nth call to HandleStartupProcInterrupts().
|
|
* This only affects how long it takes us to detect the condition while we're
|
|
* busy replaying WAL. Latch waits and similar which should react immediately
|
|
* through the usual techniques.
|
|
*/
|
|
#define POSTMASTER_POLL_RATE_LIMIT 1024
|
|
#endif
|
|
|
|
/*
|
|
* Flags set by interrupt handlers for later service in the redo loop.
|
|
*/
|
|
static volatile sig_atomic_t got_SIGHUP = false;
|
|
static volatile sig_atomic_t shutdown_requested = false;
|
|
static volatile sig_atomic_t promote_signaled = false;
|
|
|
|
/*
|
|
* Flag set when executing a restore command, to tell SIGTERM signal handler
|
|
* that it's safe to just proc_exit.
|
|
*/
|
|
static volatile sig_atomic_t in_restore_command = false;
|
|
|
|
/*
|
|
* Time at which the most recent startup operation started.
|
|
*/
|
|
static TimestampTz startup_progress_phase_start_time;
|
|
|
|
/*
|
|
* Indicates whether the startup progress interval mentioned by the user is
|
|
* elapsed or not. TRUE if timeout occurred, FALSE otherwise.
|
|
*/
|
|
static volatile sig_atomic_t startup_progress_timer_expired = false;
|
|
|
|
/*
|
|
* Time between progress updates for long-running startup operations.
|
|
*/
|
|
int log_startup_progress_interval = 10000; /* 10 sec */
|
|
|
|
/* Signal handlers */
|
|
static void StartupProcTriggerHandler(SIGNAL_ARGS);
|
|
static void StartupProcSigHupHandler(SIGNAL_ARGS);
|
|
|
|
/* Callbacks */
|
|
static void StartupProcExit(int code, Datum arg);
|
|
|
|
|
|
/* --------------------------------
|
|
* signal handler routines
|
|
* --------------------------------
|
|
*/
|
|
|
|
/* SIGUSR2: set flag to finish recovery */
|
|
static void
|
|
StartupProcTriggerHandler(SIGNAL_ARGS)
|
|
{
|
|
promote_signaled = true;
|
|
WakeupRecovery();
|
|
}
|
|
|
|
/* SIGHUP: set flag to re-read config file at next convenient time */
|
|
static void
|
|
StartupProcSigHupHandler(SIGNAL_ARGS)
|
|
{
|
|
got_SIGHUP = true;
|
|
WakeupRecovery();
|
|
}
|
|
|
|
/* SIGTERM: set flag to abort redo and exit */
|
|
static void
|
|
StartupProcShutdownHandler(SIGNAL_ARGS)
|
|
{
|
|
if (in_restore_command)
|
|
proc_exit(1);
|
|
else
|
|
shutdown_requested = true;
|
|
WakeupRecovery();
|
|
}
|
|
|
|
/*
|
|
* Re-read the config file.
|
|
*
|
|
* If one of the critical walreceiver options has changed, flag xlog.c
|
|
* to restart it.
|
|
*/
|
|
static void
|
|
StartupRereadConfig(void)
|
|
{
|
|
char *conninfo = pstrdup(PrimaryConnInfo);
|
|
char *slotname = pstrdup(PrimarySlotName);
|
|
bool tempSlot = wal_receiver_create_temp_slot;
|
|
bool conninfoChanged;
|
|
bool slotnameChanged;
|
|
bool tempSlotChanged = false;
|
|
|
|
ProcessConfigFile(PGC_SIGHUP);
|
|
|
|
conninfoChanged = strcmp(conninfo, PrimaryConnInfo) != 0;
|
|
slotnameChanged = strcmp(slotname, PrimarySlotName) != 0;
|
|
|
|
/*
|
|
* wal_receiver_create_temp_slot is used only when we have no slot
|
|
* configured. We do not need to track this change if it has no effect.
|
|
*/
|
|
if (!slotnameChanged && strcmp(PrimarySlotName, "") == 0)
|
|
tempSlotChanged = tempSlot != wal_receiver_create_temp_slot;
|
|
pfree(conninfo);
|
|
pfree(slotname);
|
|
|
|
if (conninfoChanged || slotnameChanged || tempSlotChanged)
|
|
StartupRequestWalReceiverRestart();
|
|
}
|
|
|
|
/* Handle various signals that might be sent to the startup process */
|
|
void
|
|
HandleStartupProcInterrupts(void)
|
|
{
|
|
#ifdef POSTMASTER_POLL_RATE_LIMIT
|
|
static uint32 postmaster_poll_count = 0;
|
|
#endif
|
|
|
|
/*
|
|
* Process any requests or signals received recently.
|
|
*/
|
|
if (got_SIGHUP)
|
|
{
|
|
got_SIGHUP = false;
|
|
StartupRereadConfig();
|
|
}
|
|
|
|
/*
|
|
* Check if we were requested to exit without finishing recovery.
|
|
*/
|
|
if (shutdown_requested)
|
|
proc_exit(1);
|
|
|
|
/*
|
|
* Emergency bailout if postmaster has died. This is to avoid the
|
|
* necessity for manual cleanup of all postmaster children. Do this less
|
|
* frequently on systems for which we don't have signals to make that
|
|
* cheap.
|
|
*/
|
|
if (IsUnderPostmaster &&
|
|
#ifdef POSTMASTER_POLL_RATE_LIMIT
|
|
postmaster_poll_count++ % POSTMASTER_POLL_RATE_LIMIT == 0 &&
|
|
#endif
|
|
!PostmasterIsAlive())
|
|
exit(1);
|
|
|
|
/* Process barrier events */
|
|
if (ProcSignalBarrierPending)
|
|
ProcessProcSignalBarrier();
|
|
|
|
/* Perform logging of memory contexts of this process */
|
|
if (LogMemoryContextPending)
|
|
ProcessLogMemoryContextInterrupt();
|
|
}
|
|
|
|
|
|
/* --------------------------------
|
|
* signal handler routines
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
StartupProcExit(int code, Datum arg)
|
|
{
|
|
/* Shutdown the recovery environment */
|
|
if (standbyState != STANDBY_DISABLED)
|
|
ShutdownRecoveryTransactionEnvironment();
|
|
}
|
|
|
|
|
|
/* ----------------------------------
|
|
* Startup Process main entry point
|
|
* ----------------------------------
|
|
*/
|
|
void
|
|
StartupProcessMain(char *startup_data, size_t startup_data_len)
|
|
{
|
|
Assert(startup_data_len == 0);
|
|
|
|
MyBackendType = B_STARTUP;
|
|
AuxiliaryProcessMainCommon();
|
|
|
|
/* Arrange to clean up at startup process exit */
|
|
on_shmem_exit(StartupProcExit, 0);
|
|
|
|
/*
|
|
* Properly accept or ignore signals the postmaster might send us.
|
|
*/
|
|
pqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */
|
|
pqsignal(SIGINT, SIG_IGN); /* ignore query cancel */
|
|
pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */
|
|
/* SIGQUIT handler was already set up by InitPostmasterChild */
|
|
InitializeTimeouts(); /* establishes SIGALRM handler */
|
|
pqsignal(SIGPIPE, SIG_IGN);
|
|
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
|
pqsignal(SIGUSR2, StartupProcTriggerHandler);
|
|
|
|
/*
|
|
* Reset some signals that are accepted by postmaster but not here
|
|
*/
|
|
pqsignal(SIGCHLD, SIG_DFL);
|
|
|
|
/*
|
|
* Register timeouts needed for standby mode
|
|
*/
|
|
RegisterTimeout(STANDBY_DEADLOCK_TIMEOUT, StandbyDeadLockHandler);
|
|
RegisterTimeout(STANDBY_TIMEOUT, StandbyTimeoutHandler);
|
|
RegisterTimeout(STANDBY_LOCK_TIMEOUT, StandbyLockTimeoutHandler);
|
|
|
|
/*
|
|
* Unblock signals (they were blocked when the postmaster forked us)
|
|
*/
|
|
sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
|
|
|
|
/*
|
|
* Do what we came for.
|
|
*/
|
|
StartupXLOG();
|
|
|
|
/*
|
|
* Exit normally. Exit code 0 tells postmaster that we completed recovery
|
|
* successfully.
|
|
*/
|
|
proc_exit(0);
|
|
}
|
|
|
|
void
|
|
PreRestoreCommand(void)
|
|
{
|
|
/*
|
|
* Set in_restore_command to tell the signal handler that we should exit
|
|
* right away on SIGTERM. We know that we're at a safe point to do that.
|
|
* Check if we had already received the signal, so that we don't miss a
|
|
* shutdown request received just before this.
|
|
*/
|
|
in_restore_command = true;
|
|
if (shutdown_requested)
|
|
proc_exit(1);
|
|
}
|
|
|
|
void
|
|
PostRestoreCommand(void)
|
|
{
|
|
in_restore_command = false;
|
|
}
|
|
|
|
bool
|
|
IsPromoteSignaled(void)
|
|
{
|
|
return promote_signaled;
|
|
}
|
|
|
|
void
|
|
ResetPromoteSignaled(void)
|
|
{
|
|
promote_signaled = false;
|
|
}
|
|
|
|
/*
|
|
* Set a flag indicating that it's time to log a progress report.
|
|
*/
|
|
void
|
|
startup_progress_timeout_handler(void)
|
|
{
|
|
startup_progress_timer_expired = true;
|
|
}
|
|
|
|
void
|
|
disable_startup_progress_timeout(void)
|
|
{
|
|
/* Feature is disabled. */
|
|
if (log_startup_progress_interval == 0)
|
|
return;
|
|
|
|
disable_timeout(STARTUP_PROGRESS_TIMEOUT, false);
|
|
startup_progress_timer_expired = false;
|
|
}
|
|
|
|
/*
|
|
* Set the start timestamp of the current operation and enable the timeout.
|
|
*/
|
|
void
|
|
enable_startup_progress_timeout(void)
|
|
{
|
|
TimestampTz fin_time;
|
|
|
|
/* Feature is disabled. */
|
|
if (log_startup_progress_interval == 0)
|
|
return;
|
|
|
|
startup_progress_phase_start_time = GetCurrentTimestamp();
|
|
fin_time = TimestampTzPlusMilliseconds(startup_progress_phase_start_time,
|
|
log_startup_progress_interval);
|
|
enable_timeout_every(STARTUP_PROGRESS_TIMEOUT, fin_time,
|
|
log_startup_progress_interval);
|
|
}
|
|
|
|
/*
|
|
* A thin wrapper to first disable and then enable the startup progress
|
|
* timeout.
|
|
*/
|
|
void
|
|
begin_startup_progress_phase(void)
|
|
{
|
|
/* Feature is disabled. */
|
|
if (log_startup_progress_interval == 0)
|
|
return;
|
|
|
|
disable_startup_progress_timeout();
|
|
enable_startup_progress_timeout();
|
|
}
|
|
|
|
/*
|
|
* Report whether startup progress timeout has occurred. Reset the timer flag
|
|
* if it did, set the elapsed time to the out parameters and return true,
|
|
* otherwise return false.
|
|
*/
|
|
bool
|
|
has_startup_progress_timeout_expired(long *secs, int *usecs)
|
|
{
|
|
long seconds;
|
|
int useconds;
|
|
TimestampTz now;
|
|
|
|
/* No timeout has occurred. */
|
|
if (!startup_progress_timer_expired)
|
|
return false;
|
|
|
|
/* Calculate the elapsed time. */
|
|
now = GetCurrentTimestamp();
|
|
TimestampDifference(startup_progress_phase_start_time, now, &seconds, &useconds);
|
|
|
|
*secs = seconds;
|
|
*usecs = useconds;
|
|
startup_progress_timer_expired = false;
|
|
|
|
return true;
|
|
}
|