mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Make archiver process an auxiliary process.
This commit changes WAL archiver process so that it's treated as an auxiliary process and can use shared memory. This is an infrastructure patch required for upcoming shared-memory based stats collector patch series. These patch series basically need any processes including archiver that can report the statistics to access to shared memory. Since this patch itself is useful to simplify the code and when users monitor the status of archiver, it's committed separately in advance. This commit simplifies the code for WAL archiving. For example, previously backends need to signal to archiver via postmaster when they notify archiver that there are some WAL files to archive. On the other hand, this commit removes that signal to postmaster and enables backends to notify archier directly using shared latch. Also, as the side of this change, the information about archiver process becomes viewable at pg_stat_activity view. Author: Kyotaro Horiguchi Reviewed-by: Andres Freund, Álvaro Herrera, Julien Rouhaud, Tomas Vondra, Arthur Zakirov, Fujii Masao Discussion: https://postgr.es/m/20180629.173418.190173462.horiguchi.kyotaro@lab.ntt.co.jp
This commit is contained in:
@ -38,16 +38,13 @@
|
||||
#include "libpq/pqsignal.h"
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/fork_process.h"
|
||||
#include "postmaster/interrupt.h"
|
||||
#include "postmaster/pgarch.h"
|
||||
#include "postmaster/postmaster.h"
|
||||
#include "storage/dsm.h"
|
||||
#include "storage/fd.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/latch.h"
|
||||
#include "storage/pg_shmem.h"
|
||||
#include "storage/pmsignal.h"
|
||||
#include "storage/procsignal.h"
|
||||
#include "utils/guc.h"
|
||||
#include "utils/ps_status.h"
|
||||
|
||||
@ -73,153 +70,99 @@
|
||||
*/
|
||||
#define NUM_ORPHAN_CLEANUP_RETRIES 3
|
||||
|
||||
/* Shared memory area for archiver process */
|
||||
typedef struct PgArchData
|
||||
{
|
||||
int pgprocno; /* pgprocno of archiver process */
|
||||
} PgArchData;
|
||||
|
||||
|
||||
/* ----------
|
||||
* Local data
|
||||
* ----------
|
||||
*/
|
||||
static time_t last_pgarch_start_time;
|
||||
static time_t last_sigterm_time = 0;
|
||||
static PgArchData *PgArch = NULL;
|
||||
|
||||
/*
|
||||
* Flags set by interrupt handlers for later service in the main loop.
|
||||
*/
|
||||
static volatile sig_atomic_t wakened = false;
|
||||
static volatile sig_atomic_t ready_to_stop = false;
|
||||
|
||||
/* ----------
|
||||
* Local function forward declarations
|
||||
* ----------
|
||||
*/
|
||||
#ifdef EXEC_BACKEND
|
||||
static pid_t pgarch_forkexec(void);
|
||||
#endif
|
||||
|
||||
NON_EXEC_STATIC void PgArchiverMain(int argc, char *argv[]) pg_attribute_noreturn();
|
||||
static void pgarch_waken(SIGNAL_ARGS);
|
||||
static void pgarch_waken_stop(SIGNAL_ARGS);
|
||||
static void pgarch_MainLoop(void);
|
||||
static void pgarch_ArchiverCopyLoop(void);
|
||||
static bool pgarch_archiveXlog(char *xlog);
|
||||
static bool pgarch_readyXlog(char *xlog);
|
||||
static void pgarch_archiveDone(char *xlog);
|
||||
static void pgarch_die(int code, Datum arg);
|
||||
|
||||
/* Report shared memory space needed by PgArchShmemInit */
|
||||
Size
|
||||
PgArchShmemSize(void)
|
||||
{
|
||||
Size size = 0;
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Public functions called from postmaster follow
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
size = add_size(size, sizeof(PgArchData));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Allocate and initialize archiver-related shared memory */
|
||||
void
|
||||
PgArchShmemInit(void)
|
||||
{
|
||||
bool found;
|
||||
|
||||
PgArch = (PgArchData *)
|
||||
ShmemInitStruct("Archiver Data", PgArchShmemSize(), &found);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
/* First time through, so initialize */
|
||||
MemSet(PgArch, 0, PgArchShmemSize());
|
||||
PgArch->pgprocno = INVALID_PGPROCNO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pgarch_start
|
||||
* PgArchCanRestart
|
||||
*
|
||||
* Called from postmaster at startup or after an existing archiver
|
||||
* died. Attempt to fire up a fresh archiver process.
|
||||
* Return true and archiver is allowed to restart if enough time has
|
||||
* passed since it was launched last to reach PGARCH_RESTART_INTERVAL.
|
||||
* Otherwise return false.
|
||||
*
|
||||
* Returns PID of child process, or 0 if fail.
|
||||
*
|
||||
* Note: if fail, we will be called again from the postmaster main loop.
|
||||
* This is a safety valve to protect against continuous respawn attempts if the
|
||||
* archiver is dying immediately at launch. Note that since we will retry to
|
||||
* launch the archiver from the postmaster main loop, we will get another
|
||||
* chance later.
|
||||
*/
|
||||
int
|
||||
pgarch_start(void)
|
||||
bool
|
||||
PgArchCanRestart(void)
|
||||
{
|
||||
time_t curtime;
|
||||
pid_t pgArchPid;
|
||||
static time_t last_pgarch_start_time = 0;
|
||||
time_t curtime = time(NULL);
|
||||
|
||||
/*
|
||||
* Do nothing if no archiver needed
|
||||
* Return false and don't restart archiver if too soon since last archiver
|
||||
* start.
|
||||
*/
|
||||
if (!XLogArchivingActive())
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Do nothing if too soon since last archiver start. This is a safety
|
||||
* valve to protect against continuous respawn attempts if the archiver is
|
||||
* dying immediately at launch. Note that since we will be re-called from
|
||||
* the postmaster main loop, we will get another chance later.
|
||||
*/
|
||||
curtime = time(NULL);
|
||||
if ((unsigned int) (curtime - last_pgarch_start_time) <
|
||||
(unsigned int) PGARCH_RESTART_INTERVAL)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
last_pgarch_start_time = curtime;
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
switch ((pgArchPid = pgarch_forkexec()))
|
||||
#else
|
||||
switch ((pgArchPid = fork_process()))
|
||||
#endif
|
||||
{
|
||||
case -1:
|
||||
ereport(LOG,
|
||||
(errmsg("could not fork archiver: %m")));
|
||||
return 0;
|
||||
|
||||
#ifndef EXEC_BACKEND
|
||||
case 0:
|
||||
/* in postmaster child ... */
|
||||
InitPostmasterChild();
|
||||
|
||||
/* Close the postmaster's sockets */
|
||||
ClosePostmasterPorts(false);
|
||||
|
||||
/* Drop our connection to postmaster's shared memory, as well */
|
||||
dsm_detach_all();
|
||||
PGSharedMemoryDetach();
|
||||
|
||||
PgArchiverMain(0, NULL);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return (int) pgArchPid;
|
||||
}
|
||||
|
||||
/* shouldn't get here */
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Local functions called by archiver follow
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
|
||||
/*
|
||||
* pgarch_forkexec() -
|
||||
*
|
||||
* Format up the arglist for, then fork and exec, archive process
|
||||
*/
|
||||
static pid_t
|
||||
pgarch_forkexec(void)
|
||||
{
|
||||
char *av[10];
|
||||
int ac = 0;
|
||||
|
||||
av[ac++] = "postgres";
|
||||
|
||||
av[ac++] = "--forkarch";
|
||||
|
||||
av[ac++] = NULL; /* filled in by postmaster_forkexec */
|
||||
|
||||
av[ac] = NULL;
|
||||
Assert(ac < lengthof(av));
|
||||
|
||||
return postmaster_forkexec(ac, av);
|
||||
}
|
||||
#endif /* EXEC_BACKEND */
|
||||
|
||||
|
||||
/*
|
||||
* PgArchiverMain
|
||||
*
|
||||
* The argc/argv parameters are valid only in EXEC_BACKEND case. However,
|
||||
* since we don't use 'em, it hardly matters...
|
||||
*/
|
||||
NON_EXEC_STATIC void
|
||||
PgArchiverMain(int argc, char *argv[])
|
||||
/* Main entry point for archiver process */
|
||||
void
|
||||
PgArchiverMain(void)
|
||||
{
|
||||
/*
|
||||
* Ignore all signals usually bound to some action in the postmaster,
|
||||
@ -231,33 +174,51 @@ PgArchiverMain(int argc, char *argv[])
|
||||
/* SIGQUIT handler was already set up by InitPostmasterChild */
|
||||
pqsignal(SIGALRM, SIG_IGN);
|
||||
pqsignal(SIGPIPE, SIG_IGN);
|
||||
pqsignal(SIGUSR1, pgarch_waken);
|
||||
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
||||
pqsignal(SIGUSR2, pgarch_waken_stop);
|
||||
|
||||
/* Reset some signals that are accepted by postmaster but not here */
|
||||
pqsignal(SIGCHLD, SIG_DFL);
|
||||
|
||||
/* Unblock signals (they were blocked when the postmaster forked us) */
|
||||
PG_SETMASK(&UnBlockSig);
|
||||
|
||||
MyBackendType = B_ARCHIVER;
|
||||
init_ps_display(NULL);
|
||||
/* We shouldn't be launched unnecessarily. */
|
||||
Assert(XLogArchivingActive());
|
||||
|
||||
/* Arrange to clean up at archiver exit */
|
||||
on_shmem_exit(pgarch_die, 0);
|
||||
|
||||
/*
|
||||
* Advertise our pgprocno so that backends can use our latch to wake us up
|
||||
* while we're sleeping.
|
||||
*/
|
||||
PgArch->pgprocno = MyProc->pgprocno;
|
||||
|
||||
pgarch_MainLoop();
|
||||
|
||||
exit(0);
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
/* SIGUSR1 signal handler for archiver process */
|
||||
static void
|
||||
pgarch_waken(SIGNAL_ARGS)
|
||||
/*
|
||||
* Wake up the archiver
|
||||
*/
|
||||
void
|
||||
PgArchWakeup(void)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int arch_pgprocno = PgArch->pgprocno;
|
||||
|
||||
/* set flag that there is work to be done */
|
||||
wakened = true;
|
||||
SetLatch(MyLatch);
|
||||
|
||||
errno = save_errno;
|
||||
/*
|
||||
* We don't acquire ProcArrayLock here. It's actually fine because
|
||||
* procLatch isn't ever freed, so we just can potentially set the wrong
|
||||
* process' (or no process') latch. Even in that case the archiver will
|
||||
* be relaunched shortly and will start archiving.
|
||||
*/
|
||||
if (arch_pgprocno != INVALID_PGPROCNO)
|
||||
SetLatch(&ProcGlobal->allProcs[arch_pgprocno].procLatch);
|
||||
}
|
||||
|
||||
|
||||
/* SIGUSR2 signal handler for archiver process */
|
||||
static void
|
||||
pgarch_waken_stop(SIGNAL_ARGS)
|
||||
@ -282,14 +243,6 @@ pgarch_MainLoop(void)
|
||||
pg_time_t last_copy_time = 0;
|
||||
bool time_to_stop;
|
||||
|
||||
/*
|
||||
* We run the copy loop immediately upon entry, in case there are
|
||||
* unarchived files left over from a previous database run (or maybe the
|
||||
* archiver died unexpectedly). After that we wait for a signal or
|
||||
* timeout before doing more.
|
||||
*/
|
||||
wakened = true;
|
||||
|
||||
/*
|
||||
* There shouldn't be anything for the archiver to do except to wait for a
|
||||
* signal ... however, the archiver exists to protect our data, so she
|
||||
@ -328,12 +281,8 @@ pgarch_MainLoop(void)
|
||||
}
|
||||
|
||||
/* Do what we're here for */
|
||||
if (wakened || time_to_stop)
|
||||
{
|
||||
wakened = false;
|
||||
pgarch_ArchiverCopyLoop();
|
||||
last_copy_time = time(NULL);
|
||||
}
|
||||
pgarch_ArchiverCopyLoop();
|
||||
last_copy_time = time(NULL);
|
||||
|
||||
/*
|
||||
* Sleep until a signal is received, or until a poll is forced by
|
||||
@ -354,13 +303,9 @@ pgarch_MainLoop(void)
|
||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||
timeout * 1000L,
|
||||
WAIT_EVENT_ARCHIVER_MAIN);
|
||||
if (rc & WL_TIMEOUT)
|
||||
wakened = true;
|
||||
if (rc & WL_POSTMASTER_DEATH)
|
||||
time_to_stop = true;
|
||||
}
|
||||
else
|
||||
wakened = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -744,3 +689,15 @@ pgarch_archiveDone(char *xlog)
|
||||
StatusFilePath(rlogdone, xlog, ".done");
|
||||
(void) durable_rename(rlogready, rlogdone, WARNING);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pgarch_die
|
||||
*
|
||||
* Exit-time cleanup handler
|
||||
*/
|
||||
static void
|
||||
pgarch_die(int code, Datum arg)
|
||||
{
|
||||
PgArch->pgprocno = INVALID_PGPROCNO;
|
||||
}
|
||||
|
Reference in New Issue
Block a user