From 205ea86dde898f7edac327d46b2b43b04721aadc Mon Sep 17 00:00:00 2001 From: Mike Palmiotto Date: Mon, 30 Sep 2019 14:42:53 -0400 Subject: [PATCH 7/8] Add backends to process centralization --- src/backend/postmaster/postmaster.c | 309 +++++++++++++------------- src/include/postmaster/fork_process.h | 5 + src/include/postmaster/postmaster.h | 1 - 3 files changed, 160 insertions(+), 155 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 8f862fcd64..b55cc4556d 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -144,6 +144,7 @@ static Backend *ShmemBackendArray; BackgroundWorker *MyBgworkerEntry = NULL; RegisteredBgWorker *CurrentBgWorker = NULL; +static Backend *MyBackend; static int child_errno; /* The socket number we are listening for connections on */ @@ -356,7 +357,6 @@ static void BackendInitialize(Port *port); static void BackendRun(Port *port) pg_attribute_noreturn(); static void ExitPostmaster(int status) pg_attribute_noreturn(); static int ServerLoop(void); -static int BackendStartup(Port *port); static int ProcessStartupPacket(Port *port, bool secure_done); static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options); static void processCancelRequest(Port *port, void *pkt); @@ -409,7 +409,6 @@ typedef struct } win32_deadchild_waitinfo; #endif /* WIN32 */ -static pid_t backend_forkexec(Port *port); static pid_t internal_forkexec(int argc, char *argv[]); /* Type for a socket that can be inherited to a client process */ @@ -505,6 +504,7 @@ static void ShmemBackendArrayRemove(Backend *bn); #define pgarch_start() StartChildProcess(PgArchiverFork) #define SysLogger_Start() StartChildProcess(SysLoggerFork) #define do_start_bgworker() StartChildProcess(BgWorkerFork) +#define BackendStartup() StartChildProcess(BackendFork) /* Macros to check exit status of a child process */ #define EXIT_STATUS_0(st) ((st) == 0) @@ -524,6 +524,141 @@ HANDLE PostmasterHandle; static Port *ConnProcPort = NULL; +/* + * BackendMain + * + * Child code when forking a Backend. + */ +static void BackendMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[]) +{ + /* + * Perform additional initialization and collect startup packet. + * + * We want to do this before InitProcess() for a couple of reasons: 1. + * so that we aren't eating up a PGPROC slot while waiting on the + * client. 2. so that if InitProcess() fails due to being out of + * PGPROC slots, we have already initialized libpq and are able to + * report the error to the client. + */ + BackendInitialize(ConnProcPort); + +#ifdef EXEC_BACKEND + shmemSetup(false); +#endif + + /* And run the backend */ + BackendRun(ConnProcPort); /* does not return */ +} + +/* + * BackendPostmasterMain + * + * Parent code when forking a Backend. + */ +static void BackendPostmasterMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[]) +{ + /* in parent, successful fork */ + ereport(DEBUG2, + (errmsg_internal("forked new backend, pid=%d socket=%d", + (int) MyChildProcPid, (int) MyProcPort->sock))); + + /* + * Everything's been successful, it's safe to add this backend to our list + * of backends. + */ + MyBackend->pid = MyChildProcPid; + MyBackend->bkend_type = BACKEND_TYPE_NORMAL; /* Can change later to WALSND */ + dlist_push_head(&BackendList, &MyBackend->elem); + +#ifdef EXEC_BACKEND + if (!MyBackend->dead_end) + ShmemBackendArrayAdd(MyBackend); +#endif +} + +/* + * BackendFailFork + * + * Backend cleanup in case a failure occurs forking a new Backend. + */ +static void BackendFailFork(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[]) +{ + if (!MyBackend->dead_end) + (void) ReleasePostmasterChildSlot(MyBackend->child_slot); + free(MyBackend); + + report_fork_failure_to_client(MyProcPort, child_errno); +} + +/* + * PrepBackendFork + * + * Prepare a ForkProcType struct for starting a Backend. + * This does all prep related to av parameters and error messages. +*/ +static void +PrepBackendFork(ForkProcData *backend_fork) +{ + int ac = 0; + + /* + * Create backend data structure. Better before the fork() so we can + * handle failure cleanly. + */ + MyBackend = (Backend *) malloc(sizeof(Backend)); + if (!MyBackend) + { + ereport(LOG, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + } + +#ifdef EXEC_BACKEND + backend_fork->av[ac++] = pstrdup("postgres"); + backend_fork->av[ac++] = pstrdup("--forkbackend"); + backend_fork->av[ac++] = NULL; +#endif + backend_fork->av[ac] = NULL; + backend_fork->ac = ac; + + Assert(ac < lengthof(*backend_fork->av)); + + /* + * Compute the cancel key that will be assigned to this backend. The + * backend will have its own copy in the forked-off process' value of + * MyCancelKey, so that it can transmit the key to the frontend. + */ + if (!RandomCancelKey(&MyCancelKey)) + { + free(MyBackend); + ereport(LOG, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random cancel key"))); + } + + MyBackend->cancel_key = MyCancelKey; + + /* Pass down canAcceptConnections state */ + ConnProcPort->canAcceptConnections = canAcceptConnections(); + MyBackend->dead_end = (ConnProcPort->canAcceptConnections != CAC_OK && + ConnProcPort->canAcceptConnections != CAC_WAITBACKUP); + + /* + * Unless it's a dead_end child, assign it a child slot number + */ + if (!MyBackend->dead_end) + MyBackend->child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); + else + MyBackend->child_slot = 0; + + /* Hasn't asked to be notified about any bgworkers yet */ + MyBackend->bgworker_notify = false; + + backend_fork->postmaster_main = BackendPostmasterMain; + backend_fork->child_main = BackendMain; + backend_fork->fail_main = BackendFailFork; +} + /* * PrepAuxProcessFork * @@ -1729,7 +1864,7 @@ ServerLoop(void) ConnProcPort = ConnCreate(ListenSocket[i]); if (ConnProcPort) { - BackendStartup(ConnProcPort); + BackendStartup(); /* * We no longer need the open socket or port structure @@ -4085,122 +4220,6 @@ TerminateChildren(int signal) signal_child(PgStatPID, signal); } -/* - * BackendStartup -- start backend process - * - * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise. - * - * Note: if you change this code, also consider StartAutovacuumWorker. - */ -static int -BackendStartup(Port *port) -{ - Backend *bn; /* for backend cleanup */ - pid_t pid; - - /* - * Create backend data structure. Better before the fork() so we can - * handle failure cleanly. - */ - bn = (Backend *) malloc(sizeof(Backend)); - if (!bn) - { - ereport(LOG, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - return STATUS_ERROR; - } - - /* - * Compute the cancel key that will be assigned to this backend. The - * backend will have its own copy in the forked-off process' value of - * MyCancelKey, so that it can transmit the key to the frontend. - */ - if (!RandomCancelKey(&MyCancelKey)) - { - free(bn); - ereport(LOG, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("could not generate random cancel key"))); - return STATUS_ERROR; - } - - bn->cancel_key = MyCancelKey; - - /* 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 - */ - if (!bn->dead_end) - bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); - else - bn->child_slot = 0; - - /* Hasn't asked to be notified about any bgworkers yet */ - bn->bgworker_notify = false; - -#ifdef EXEC_BACKEND - pid = backend_forkexec(port); -#else /* !EXEC_BACKEND */ - pid = fork_process(); - if (pid == 0) /* child */ - { - free(bn); - - /* Detangle from postmaster */ - InitPostmasterChild(); - - /* Close the postmaster's sockets */ - ClosePostmasterPorts(false); - - /* Perform additional initialization and collect startup packet */ - BackendInitialize(port); - - /* And run the backend */ - BackendRun(port); - } -#endif /* EXEC_BACKEND */ - - if (pid < 0) - { - /* in parent, fork failed */ - int save_errno = errno; - - if (!bn->dead_end) - (void) ReleasePostmasterChildSlot(bn->child_slot); - free(bn); - errno = save_errno; - ereport(LOG, - (errmsg("could not fork new process for connection: %m"))); - report_fork_failure_to_client(port, save_errno); - return STATUS_ERROR; - } - - /* in parent, successful fork */ - ereport(DEBUG2, - (errmsg_internal("forked new backend, pid=%d socket=%d", - (int) pid, (int) port->sock))); - - /* - * Everything's been successful, it's safe to add this backend to our list - * of backends. - */ - bn->pid = pid; - bn->bkend_type = BACKEND_TYPE_NORMAL; /* Can change later to WALSND */ - dlist_push_head(&BackendList, &bn->elem); - -#ifdef EXEC_BACKEND - if (!bn->dead_end) - ShmemBackendArrayAdd(bn); -#endif - - return STATUS_OK; -} - /* * Try to report backend fork() failure to client before we close the * connection. Since we do not care to risk blocking the postmaster on @@ -4440,7 +4459,7 @@ BackendRun(Port *port) maxac * sizeof(char *)); ac = 0; - av[ac++] = "postgres"; + av[ac++] = pstrdup("postgres"); /* * Pass any backend switches specified with -o on the postmaster's own @@ -4489,38 +4508,13 @@ BackendRun(Port *port) * child process. */ pid_t -postmaster_forkexec(int argc, char *argv[]) +postmaster_forkexec(ForkProcData *fork_data) { /* This entry point passes dummy values for the Port variables */ if (!ConnProcPort) ConnProcPort = palloc0(sizeof(*ConnProcPort)); - return internal_forkexec(argc, argv); -} - -/* - * backend_forkexec -- fork/exec off a backend process - * - * Some operating systems (WIN32) don't have fork() so we have to simulate - * it by storing parameters that need to be passed to the child and - * then create a new child process. - * - * returns the pid of the fork/exec'd process, or -1 on failure - */ -static pid_t -backend_forkexec(Port *port) -{ - char *av[4]; - int ac = 0; - - av[ac++] = "postgres"; - av[ac++] = "--forkbackend"; - av[ac++] = NULL; /* filled in by internal_forkexec */ - - av[ac] = NULL; - Assert(ac < lengthof(av)); - - return internal_forkexec(ac, av); + return internal_forkexec(fork_data->ac, fork_data->av); } #ifndef WIN32 @@ -5413,6 +5407,13 @@ StartChildProcess(ForkProcType type) break; case PgArchiverFork: PrepPgArchiverFork(fork_data); + break; + case SysLoggerFork: + if (!Logging_collector) + return 0; + + PrepSysLoggerFork(fork_data); + break; case BgWorkerFork: if (PrepBgWorkerFork(fork_data)) { @@ -5422,11 +5423,8 @@ StartChildProcess(ForkProcType type) return -1; } break; - case SysLoggerFork: - if (!Logging_collector) - return 0; - - PrepSysLoggerFork(fork_data); + case BackendFork: + PrepBackendFork(fork_data); break; default: break; @@ -5439,7 +5437,7 @@ StartChildProcess(ForkProcType type) return 0; #ifdef EXEC_BACKEND - pid = postmaster_forkexec(fork_data->ac, fork_data->av); + pid = postmaster_forkexec(fork_data); #else pid = fork_process(); #endif @@ -5453,10 +5451,11 @@ StartChildProcess(ForkProcType type) InitPostmasterChild(); /* Release postmaster's working memory context */ - if (type != SysLoggerFork) + if (type != SysLoggerFork && type != BackendFork) { /* Close the postmaster's sockets */ ClosePostmasterPorts(false); + if (type == BgWorkerFork) { /* @@ -5504,6 +5503,7 @@ StartChildProcess(ForkProcType type) ExitPostmaster(1); break; case BgWorkerFork: + case BackendFork: fork_data->fail_main(fork_data->ac, fork_data->av); return -1; default: @@ -5518,6 +5518,7 @@ StartChildProcess(ForkProcType type) { case SysLoggerFork: case BgWorkerFork: + case BackendFork: fork_data->postmaster_main(fork_data->ac, fork_data->av); break; default: diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h index b5d5457ce7..631a2113b5 100644 --- a/src/include/postmaster/fork_process.h +++ b/src/include/postmaster/fork_process.h @@ -30,6 +30,7 @@ typedef enum PgArchiverFork, SysLoggerFork, BgWorkerFork, + BackendFork, NUMFORKPROCTYPES /* Must be last! */ } ForkProcType; @@ -53,4 +54,8 @@ typedef struct ForkProcData extern pid_t fork_process(void); +#ifdef EXEC_BACKEND +extern pid_t postmaster_forkexec(ForkProcData *fork_data); +#endif + #endif /* FORK_PROCESS_H */ diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 9962bdd4be..a52952b74e 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -92,7 +92,6 @@ extern bool PostmasterMarkPIDForWorkerNotify(int); extern bool RandomCancelKey(int32 *cancel_key); #ifdef EXEC_BACKEND -extern pid_t postmaster_forkexec(int argc, char *argv[]); extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn(); extern Size ShmemBackendArraySize(void); -- 2.23.0