*
* Copyright (c) 2001-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.74 2004/06/03 02:08:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.75 2004/06/14 18:08:18 tgl Exp $
* ----------
*/
#include "postgres.h"
bool pgstat_collect_tuplelevel = false;
bool pgstat_collect_blocklevel = false;
-/* ----------
- * Other global variables
- * ----------
- */
-bool pgstat_is_running = false;
-
/* ----------
* Local data
* ----------
static int pgStatPipe[2];
static struct sockaddr_storage pgStatAddr;
-static int pgStatPid;
static time_t last_pgstat_start_time;
static long pgStatNumMessages = 0;
NON_EXEC_STATIC void PgstatBufferMain(int argc, char *argv[]);
NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]);
static void pgstat_recvbuffer(void);
+static void pgstat_exit(SIGNAL_ARGS);
static void pgstat_die(SIGNAL_ARGS);
static int pgstat_add_backend(PgStat_MsgHdr *msg);
* Called from postmaster at startup or after an existing collector
* died. Attempt to fire up a fresh statistics collector.
*
+ * Returns PID of child process, or 0 if fail.
+ *
* Note: if fail, we will be called again from the postmaster main loop.
* ----------
*/
-void
+int
pgstat_start(void)
{
time_t curtime;
+ pid_t pgStatPid;
/*
* Do nothing if no collector needed
*/
- if (pgstat_is_running || !pgstat_collect_startcollector)
- return;
+ if (!pgstat_collect_startcollector)
+ return 0;
/*
* Do nothing if too soon since last collector start. This is a
curtime = time(NULL);
if ((unsigned int) (curtime - last_pgstat_start_time) <
(unsigned int) PGSTAT_RESTART_INTERVAL)
- return;
+ return 0;
last_pgstat_start_time = curtime;
/*
* pgstat_collect_startcollector on after it had been off.
*/
pgstat_collect_startcollector = false;
- return;
+ return 0;
}
/*
- * Okay, fork off the collector. Remember its PID for
- * pgstat_ispgstat.
+ * Okay, fork off the collector.
*/
fflush(stdout);
#endif
#ifdef EXEC_BACKEND
- switch ((pgStatPid = (int) pgstat_forkexec(STAT_PROC_BUFFER)))
+ switch ((pgStatPid = pgstat_forkexec(STAT_PROC_BUFFER)))
#else
- switch ((pgStatPid = (int) fork()))
+ switch ((pgStatPid = fork()))
#endif
{
case -1:
#endif
ereport(LOG,
(errmsg("could not fork statistics buffer: %m")));
- return;
+ return 0;
#ifndef EXEC_BACKEND
case 0:
#endif
default:
- pgstat_is_running = true;
- return;
+ return (int) pgStatPid;
}
-}
-
-/* ----------
- * pgstat_ispgstat() -
- *
- * Called from postmaster to check if a terminated child process
- * was the statistics collector.
- * ----------
- */
-bool
-pgstat_ispgstat(int pid)
-{
- if (!pgstat_is_running)
- return false;
-
- if (pgStatPid != pid)
- return false;
-
- /* Oh dear ... */
- pgstat_is_running = false;
-
- return true;
+ /* shouldn't get here */
+ return 0;
}
/*
* Ignore all signals usually bound to some action in the postmaster,
- * except for SIGCHLD --- see pgstat_recvbuffer.
+ * except for SIGCHLD and SIGQUIT --- see pgstat_recvbuffer.
*/
pqsignal(SIGHUP, SIG_IGN);
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGTERM, SIG_IGN);
- pqsignal(SIGQUIT, SIG_IGN);
+ pqsignal(SIGQUIT, pgstat_exit);
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, SIG_IGN);
/*
* Reset signal handling. With the exception of restoring default
- * SIGCHLD handling, this is a no-op in the non-EXEC_BACKEND case
- * because we'll have inherited these settings from the buffer process;
- * but it's not a no-op for EXEC_BACKEND.
+ * SIGCHLD and SIGQUIT handling, this is a no-op in the non-EXEC_BACKEND
+ * case because we'll have inherited these settings from the buffer
+ * process; but it's not a no-op for EXEC_BACKEND.
*/
pqsignal(SIGHUP, SIG_IGN);
pqsignal(SIGINT, SIG_IGN);
}
/*
- * Wait for some work to do; but not for more than 10 seconds
- * (this determines how quickly we will shut down after postmaster
- * termination).
+ * Wait for some work to do; but not for more than 10 seconds.
+ * (This determines how quickly we will shut down after an
+ * ungraceful postmaster termination; so it needn't be very fast.)
*/
timeout.tv_sec = 10;
timeout.tv_usec = 0;
/*
* Make sure we forwarded all messages before we check for
- * Postmaster termination.
+ * postmaster termination.
*/
if (msg_have != 0 || FD_ISSET(pgStatSock, &rfds))
continue;
/*
- * If the postmaster has terminated, we've done our job.
+ * If the postmaster has terminated, we die too. (This is no longer
+ * the normal exit path, however.)
*/
if (!PostmasterIsAlive(true))
exit(0);
}
}
+/* SIGQUIT signal handler for buffer process */
+static void
+pgstat_exit(SIGNAL_ARGS)
+{
+ /*
+ * For now, we just nail the doors shut and get out of town. It might
+ * be cleaner to allow any pending messages to be sent, but that creates
+ * a tradeoff against speed of exit.
+ */
+ exit(0);
+}
+
+/* SIGCHLD signal handler for buffer process */
static void
pgstat_die(SIGNAL_ARGS)
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.403 2004/06/11 03:54:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.404 2004/06/14 18:08:19 tgl Exp $
*
* NOTES
*
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
- BgWriterPID = 0;
+ BgWriterPID = 0,
+ PgStatPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
whereToSendOutput = None;
/*
- * Initialize and try to startup the statistics collector process
+ * Initialize the statistics collector stuff
*/
pgstat_init();
- pgstat_start();
/*
* Load cached files for client authentication.
}
/* If we have lost the stats collector, try to start a new one */
- if (!pgstat_is_running)
- pgstat_start();
+ if (PgStatPID == 0 &&
+ StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ PgStatPID = pgstat_start();
/*
* Touch the socket and lock file at least every ten minutes, to ensure
backendPID = (int) ntohl(canc->backendPID);
cancelAuthCode = (long) ntohl(canc->cancelAuthCode);
- if (backendPID == BgWriterPID)
- {
- ereport(DEBUG2,
- (errmsg_internal("ignoring cancel request for bgwriter process %d",
- backendPID)));
- return;
- }
-
/*
* See if we have a matching backend. In the EXEC_BACKEND case, we
* can no longer access the postmaster's own backend list, and must
SignalChildren(SIGHUP);
if (BgWriterPID != 0)
kill(BgWriterPID, SIGHUP);
+ /* PgStatPID does not currently need SIGHUP */
load_hba();
load_ident();
/* And tell it to shut down */
if (BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ /* Tell pgstat to shut down too; nothing left for it to do */
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
break;
case SIGINT:
/* And tell it to shut down */
if (BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ /* Tell pgstat to shut down too; nothing left for it to do */
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
break;
case SIGQUIT:
kill(StartupPID, SIGQUIT);
if (BgWriterPID != 0)
kill(BgWriterPID, SIGQUIT);
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
if (DLGetHead(BackendList))
SignalChildren(SIGQUIT);
ExitPostmaster(0);
#endif /* WIN32 */
#endif /* HAVE_WAITPID */
- /*
- * Check if this child was the statistics collector. If so, try to
- * start a new one. (If fail, we'll try again in future cycles of
- * the main loop.)
- */
- if (pgstat_ispgstat(pid))
- {
- LogChildExit(LOG, gettext("statistics collector process"),
- pid, exitstatus);
- pgstat_start();
- continue;
- }
-
/*
* Check if this child was a startup process.
*/
/*
* Go to shutdown mode if a shutdown request was pending.
+ * Otherwise, try to start the stats collector too.
*/
if (Shutdown > NoShutdown && BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ else if (PgStatPID == 0 && Shutdown == NoShutdown)
+ PgStatPID = pgstat_start();
continue;
}
*/
if (BgWriterPID != 0 && pid == BgWriterPID)
{
+ BgWriterPID = 0;
if (exitstatus == 0 && Shutdown > NoShutdown &&
!FatalError && !DLGetHead(BackendList))
{
/*
* Any unexpected exit of the bgwriter is treated as a crash.
*/
- LogChildExit(DEBUG2, gettext("background writer process"),
+ LogChildExit(LOG, gettext("background writer process"),
pid, exitstatus);
HandleChildCrash(pid, exitstatus);
continue;
}
+ /*
+ * Was it the statistics collector? If so, just try to start a new
+ * one; no need to force reset of the rest of the system. (If fail,
+ * we'll try again in future cycles of the main loop.)
+ */
+ if (PgStatPID != 0 && pid == PgStatPID)
+ {
+ PgStatPID = 0;
+ if (exitstatus != 0)
+ LogChildExit(LOG, gettext("statistics collector process"),
+ pid, exitstatus);
+ if (StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ PgStatPID = pgstat_start();
+ continue;
+ }
+
/*
* Else do standard backend child cleanup.
*/
kill(BgWriterPID, (SendStop ? SIGSTOP : SIGQUIT));
}
+ /* Force a power-cycle of the pgstat processes too */
+ /* (Shouldn't be necessary, but just for luck) */
+ if (PgStatPID != 0 && !FatalError)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ "SIGQUIT",
+ (int) PgStatPID)));
+ kill(PgStatPID, SIGQUIT);
+ }
+
FatalError = true;
}
}
/*
- * Send a signal to all backend children.
+ * Send a signal to all backend children (but NOT special children)
*/
static void
SignalChildren(int signal)
}
/*
- * Count up number of child processes.
+ * Count up number of child processes (regular backends only)
*/
static int
CountChildren(void)
*
* Copyright (c) 2001-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.23 2004/05/29 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.24 2004/06/14 18:08:19 tgl Exp $
* ----------
*/
#ifndef PGSTAT_H
extern bool pgstat_collect_tuplelevel;
extern bool pgstat_collect_blocklevel;
-/* ----------
- * Other global variables
- * ----------
- */
-extern bool pgstat_is_running;
-
/* ----------
* Functions called from postmaster
* ----------
*/
extern void pgstat_init(void);
-extern void pgstat_start(void);
-extern bool pgstat_ispgstat(int pid);
+extern int pgstat_start(void);
extern void pgstat_beterm(int pid);
#ifdef EXEC_BACKEND