diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 11cbb5ff339..21275f9d5f0 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1,4 +1,4 @@ - + Monitoring Database Activity @@ -50,7 +50,7 @@ $ ps auxww | grep ^postgres postgres 960 0.0 1.1 6104 1480 pts/1 SN 13:17 0:00 postgres -i -postgres 963 0.0 1.1 7084 1472 pts/1 SN 13:17 0:00 postgres: stats buffer process +postgres 963 0.0 1.1 7084 1472 pts/1 SN 13:17 0:00 postgres: writer process postgres 965 0.0 1.1 6152 1512 pts/1 SN 13:17 0:00 postgres: stats collector process postgres 998 0.0 2.3 6532 2992 pts/1 SN 13:18 0:00 postgres: tgl runbug 127.0.0.1 idle postgres 1003 0.0 2.4 6532 3128 pts/1 SN 13:19 0:00 postgres: tgl regression [local] SELECT waiting @@ -60,10 +60,11 @@ postgres 1016 0.1 2.4 6532 3080 pts/1 SN 13:19 0:00 postgres: tgl reg (The appropriate invocation of ps varies across different platforms, as do the details of what is shown. This example is from a recent Linux system.) The first process listed here is the - the master server process. The command arguments + master server process. The command arguments shown for it are the same ones given when it was launched. The next two - processes implement the statistics collector, which will be described in - detail in the next section. (These will not be present if you have set + processes are background worker processes automatically launched by the + master process. (The stats collector process will not be present + if you have set the system not to start the statistics collector.) Each of the remaining processes is a server process handling one client connection. Each such process sets its command line display in the form @@ -83,6 +84,13 @@ postgres: user database host + + If you have turned off then the + activity indicator is not updated; the process title is set only once + when a new process is launched. On some platforms this saves a useful + amount of per-command overhead, on others it's insignificant. + + Solaris requires special handling. You must diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 34ae644351d..cac931dc88e 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -13,7 +13,7 @@ * * Copyright (c) 2001-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.132 2006/06/27 22:16:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.133 2006/06/29 20:00:08 tgl Exp $ * ---------- */ #include "postgres.h" @@ -28,6 +28,12 @@ #include #include #include +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#endif #include "pgstat.h" @@ -73,11 +79,9 @@ * failed statistics collector; in * seconds. */ -/* ---------- - * Amount of space reserved in pgstat_recvbuffer(). - * ---------- - */ -#define PGSTAT_RECVBUFFERSZ ((int) (1024 * sizeof(PgStat_Msg))) +#define PGSTAT_SELECT_TIMEOUT 2 /* How often to check for postmaster + * death; in seconds. */ + /* ---------- * The initial size hints for the hash tables used in the collector. @@ -102,12 +106,9 @@ bool pgstat_collect_querystring = false; * ---------- */ NON_EXEC_STATIC int pgStatSock = -1; -NON_EXEC_STATIC int pgStatPipe[2] = {-1, -1}; static struct sockaddr_storage pgStatAddr; -static pid_t pgStatCollectorPid = 0; - static time_t last_pgstat_start_time; static bool pgStatRunningInCollector = false; @@ -138,7 +139,8 @@ static TransactionId pgStatLocalStatusXact = InvalidTransactionId; static PgBackendStatus *localBackendStatusTable = NULL; static int localNumBackends = 0; -static volatile bool need_statwrite; +static volatile bool need_exit = false; +static volatile bool need_statwrite = false; /* ---------- @@ -146,23 +148,12 @@ static volatile bool need_statwrite; * ---------- */ #ifdef EXEC_BACKEND - -typedef enum STATS_PROCESS_TYPE -{ - STAT_PROC_BUFFER, - STAT_PROC_COLLECTOR -} STATS_PROCESS_TYPE; - -static pid_t pgstat_forkexec(STATS_PROCESS_TYPE procType); -static void pgstat_parseArgs(int argc, char *argv[]); +static pid_t pgstat_forkexec(void); #endif -NON_EXEC_STATIC void PgstatBufferMain(int argc, char *argv[]); NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]); -static void force_statwrite(SIGNAL_ARGS); -static void pgstat_recvbuffer(void); static void pgstat_exit(SIGNAL_ARGS); -static void pgstat_die(SIGNAL_ARGS); +static void force_statwrite(SIGNAL_ARGS); static void pgstat_beshutdown_hook(int code, Datum arg); static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create); @@ -417,9 +408,8 @@ pgstat_init(void) /* * Set the socket to non-blocking IO. This ensures that if the collector - * falls behind (despite the buffering process), statistics messages will - * be discarded; backends won't block waiting to send messages to the - * collector. + * falls behind, statistics messages will be discarded; backends won't + * block waiting to send messages to the collector. */ if (!pg_set_noblock(pgStatSock)) { @@ -468,65 +458,24 @@ pgstat_reset_all(void) /* * pgstat_forkexec() - * - * Format up the arglist for, then fork and exec, statistics - * (buffer and collector) processes + * Format up the arglist for, then fork and exec, statistics collector process */ static pid_t -pgstat_forkexec(STATS_PROCESS_TYPE procType) +pgstat_forkexec(void) { char *av[10]; - int ac = 0, - bufc = 0, - i; - char pgstatBuf[2][32]; + int ac = 0; av[ac++] = "postgres"; - - switch (procType) - { - case STAT_PROC_BUFFER: - av[ac++] = "--forkbuf"; - break; - - case STAT_PROC_COLLECTOR: - av[ac++] = "--forkcol"; - break; - - default: - Assert(false); - } - + av[ac++] = "--forkcol"; av[ac++] = NULL; /* filled in by postmaster_forkexec */ - /* postgres_exec_path is not passed by write_backend_variables */ - av[ac++] = postgres_exec_path; - - /* Add to the arg list */ - Assert(bufc <= lengthof(pgstatBuf)); - for (i = 0; i < bufc; i++) - av[ac++] = pgstatBuf[i]; - av[ac] = NULL; Assert(ac < lengthof(av)); return postmaster_forkexec(ac, av); } - -/* - * pgstat_parseArgs() - - * - * Extract data from the arglist for exec'ed statistics - * (buffer and collector) processes - */ -static void -pgstat_parseArgs(int argc, char *argv[]) -{ - Assert(argc == 4); - - argc = 3; - StrNCpy(postgres_exec_path, argv[argc++], MAXPGPATH); -} #endif /* EXEC_BACKEND */ @@ -585,14 +534,14 @@ pgstat_start(void) * Okay, fork off the collector. */ #ifdef EXEC_BACKEND - switch ((pgStatPid = pgstat_forkexec(STAT_PROC_BUFFER))) + switch ((pgStatPid = pgstat_forkexec())) #else switch ((pgStatPid = fork_process())) #endif { case -1: ereport(LOG, - (errmsg("could not fork statistics buffer: %m"))); + (errmsg("could not fork statistics collector: %m"))); return 0; #ifndef EXEC_BACKEND @@ -607,7 +556,7 @@ pgstat_start(void) /* Drop our connection to postmaster's shared memory, as well */ PGSharedMemoryDetach(); - PgstatBufferMain(0, NULL); + PgstatCollectorMain(0, NULL); break; #endif @@ -1602,94 +1551,11 @@ pgstat_send(void *msg, int len) } -/* ---------- - * PgstatBufferMain() - - * - * Start up the statistics buffer process. This is the body of the - * postmaster child process. - * - * The argc/argv parameters are valid only in EXEC_BACKEND case. - * ---------- - */ -NON_EXEC_STATIC void -PgstatBufferMain(int argc, char *argv[]) -{ - IsUnderPostmaster = true; /* we are a postmaster subprocess now */ - - MyProcPid = getpid(); /* reset MyProcPid */ - - /* - * Ignore all signals usually bound to some action in the postmaster, - * except for SIGCHLD and SIGQUIT --- see pgstat_recvbuffer. - */ - pqsignal(SIGHUP, SIG_IGN); - pqsignal(SIGINT, SIG_IGN); - pqsignal(SIGTERM, SIG_IGN); - pqsignal(SIGQUIT, pgstat_exit); - pqsignal(SIGALRM, SIG_IGN); - pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, SIG_IGN); - pqsignal(SIGUSR2, SIG_IGN); - pqsignal(SIGCHLD, pgstat_die); - pqsignal(SIGTTIN, SIG_DFL); - pqsignal(SIGTTOU, SIG_DFL); - pqsignal(SIGCONT, SIG_DFL); - pqsignal(SIGWINCH, SIG_DFL); - /* unblock will happen in pgstat_recvbuffer */ - -#ifdef EXEC_BACKEND - pgstat_parseArgs(argc, argv); -#endif - - /* - * Start a buffering process to read from the socket, so we have a little - * more time to process incoming messages. - * - * NOTE: the process structure is: postmaster is parent of buffer process - * is parent of collector process. This way, the buffer can detect - * collector failure via SIGCHLD, whereas otherwise it wouldn't notice - * collector failure until it tried to write on the pipe. That would mean - * that after the postmaster started a new collector, we'd have two buffer - * processes competing to read from the UDP socket --- not good. - */ - if (pgpipe(pgStatPipe) < 0) - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("could not create pipe for statistics buffer: %m"))); - - /* child becomes collector process */ -#ifdef EXEC_BACKEND - pgStatCollectorPid = pgstat_forkexec(STAT_PROC_COLLECTOR); -#else - pgStatCollectorPid = fork(); -#endif - switch (pgStatCollectorPid) - { - case -1: - ereport(ERROR, - (errmsg("could not fork statistics collector: %m"))); - -#ifndef EXEC_BACKEND - case 0: - /* child becomes collector process */ - PgstatCollectorMain(0, NULL); - break; -#endif - - default: - /* parent becomes buffer process */ - closesocket(pgStatPipe[0]); - pgstat_recvbuffer(); - } - exit(0); -} - - /* ---------- * PgstatCollectorMain() - * - * Start up the statistics collector itself. This is the body of the - * postmaster grandchild process. + * Start up the statistics collector process. This is the body of the + * postmaster child process. * * The argc/argv parameters are valid only in EXEC_BACKEND case. * ---------- @@ -1697,30 +1563,29 @@ PgstatBufferMain(int argc, char *argv[]) NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]) { - PgStat_Msg msg; - fd_set rfds; - int readPipe; - int len = 0; - struct itimerval timeout; + struct itimerval write_timeout; bool need_timer = false; + int len; + PgStat_Msg msg; +#ifdef HAVE_POLL + struct pollfd input_fd; +#else + struct timeval sel_timeout; + fd_set rfds; +#endif + + IsUnderPostmaster = true; /* we are a postmaster subprocess now */ MyProcPid = getpid(); /* reset MyProcPid */ /* - * Reset signal handling. With the exception of restoring default 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. + * Ignore all signals usually bound to some action in the postmaster, + * except SIGQUIT and SIGALRM. */ pqsignal(SIGHUP, SIG_IGN); pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, SIG_IGN); -#ifndef WIN32 - pqsignal(SIGQUIT, SIG_IGN); -#else - /* kluge to allow buffer process to kill collector; FIXME */ pqsignal(SIGQUIT, pgstat_exit); -#endif pqsignal(SIGALRM, force_statwrite); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, SIG_IGN); @@ -1732,14 +1597,6 @@ PgstatCollectorMain(int argc, char *argv[]) pqsignal(SIGWINCH, SIG_DFL); PG_SETMASK(&UnBlockSig); -#ifdef EXEC_BACKEND - pgstat_parseArgs(argc, argv); -#endif - - /* Close unwanted files */ - closesocket(pgStatPipe[1]); - closesocket(pgStatSock); - /* * Identify myself via ps */ @@ -1751,9 +1608,9 @@ PgstatCollectorMain(int argc, char *argv[]) need_statwrite = true; /* Preset the delay between status file writes */ - MemSet(&timeout, 0, sizeof(struct itimerval)); - timeout.it_value.tv_sec = PGSTAT_STAT_INTERVAL / 1000; - timeout.it_value.tv_usec = PGSTAT_STAT_INTERVAL % 1000; + MemSet(&write_timeout, 0, sizeof(struct itimerval)); + write_timeout.it_value.tv_sec = PGSTAT_STAT_INTERVAL / 1000; + write_timeout.it_value.tv_usec = PGSTAT_STAT_INTERVAL % 1000; /* * Read in an existing statistics stats file or initialize the stats to @@ -1762,14 +1619,32 @@ PgstatCollectorMain(int argc, char *argv[]) pgStatRunningInCollector = true; pgstat_read_statsfile(&pgStatDBHash, InvalidOid); - readPipe = pgStatPipe[0]; + /* + * Setup the descriptor set for select(2). Since only one bit in the + * set ever changes, we need not repeat FD_ZERO each time. + */ +#ifndef HAVE_POLL + FD_ZERO(&rfds); +#endif /* - * Process incoming messages and handle all the reporting stuff until - * there are no more messages. + * Loop to process messages until we get SIGQUIT or detect ungraceful + * death of our parent postmaster. + * + * For performance reasons, we don't want to do a PostmasterIsAlive() + * test after every message; instead, do it at statwrite time and if + * select()/poll() is interrupted by timeout. */ for (;;) { + int got_data; + + /* + * Quit if we get SIGQUIT from the postmaster. + */ + if (need_exit) + break; + /* * If time to write the stats file, do so. Note that the alarm * interrupt isn't re-enabled immediately, but only after we next @@ -1778,21 +1653,53 @@ PgstatCollectorMain(int argc, char *argv[]) */ if (need_statwrite) { + /* Check for postmaster death; if so we'll write file below */ + if (!PostmasterIsAlive(true)) + break; + pgstat_write_statsfile(); need_statwrite = false; need_timer = true; } /* - * Setup the descriptor set for select(2) + * Wait for a message to arrive; but not for more than + * PGSTAT_SELECT_TIMEOUT seconds. (This determines how quickly we will + * shut down after an ungraceful postmaster termination; so it needn't + * be very fast. However, on some systems SIGQUIT won't interrupt + * the poll/select call, so this also limits speed of response to + * SIGQUIT, which is more important.) + * + * We use poll(2) if available, otherwise select(2) */ - FD_ZERO(&rfds); - FD_SET(readPipe, &rfds); +#ifdef HAVE_POLL + input_fd.fd = pgStatSock; + input_fd.events = POLLIN | POLLERR; + input_fd.revents = 0; + + if (poll(&input_fd, 1, PGSTAT_SELECT_TIMEOUT * 1000) < 0) + { + if (errno == EINTR) + continue; + ereport(ERROR, + (errcode_for_socket_access(), + errmsg("poll() failed in statistics collector: %m"))); + } + + got_data = (input_fd.revents != 0); + +#else /* !HAVE_POLL */ + + FD_SET(pgStatSock, &rfds); /* - * Now wait for something to do. + * timeout struct is modified by select() on some operating systems, + * so re-fill it each time. */ - if (select(readPipe + 1, &rfds, NULL, NULL, NULL) < 0) + sel_timeout.tv_sec = PGSTAT_SELECT_TIMEOUT; + sel_timeout.tv_usec = 0; + + if (select(pgStatSock + 1, &rfds, NULL, NULL, &sel_timeout) < 0) { if (errno == EINTR) continue; @@ -1801,272 +1708,17 @@ PgstatCollectorMain(int argc, char *argv[]) errmsg("select() failed in statistics collector: %m"))); } - /* - * Check if there is a new statistics message to collect. - */ - if (FD_ISSET(readPipe, &rfds)) - { - /* - * We may need to issue multiple read calls in case the buffer - * process didn't write the message in a single write, which is - * possible since it dumps its buffer bytewise. In any case, we'd - * need two reads since we don't know the message length - * initially. - */ - int nread = 0; - int targetlen = sizeof(PgStat_MsgHdr); /* initial */ - bool pipeEOF = false; + got_data = FD_ISSET(pgStatSock, &rfds); - while (nread < targetlen) - { - len = piperead(readPipe, ((char *) &msg) + nread, - targetlen - nread); - if (len < 0) - { - if (errno == EINTR) - continue; - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("could not read from statistics collector pipe: %m"))); - } - if (len == 0) /* EOF on the pipe! */ - { - pipeEOF = true; - break; - } - nread += len; - if (nread == sizeof(PgStat_MsgHdr)) - { - /* we have the header, compute actual msg length */ - targetlen = msg.msg_hdr.m_size; - if (targetlen < (int) sizeof(PgStat_MsgHdr) || - targetlen > (int) sizeof(msg)) - { - /* - * Bogus message length implies that we got out of - * sync with the buffer process somehow. Abort so that - * we can restart both processes. - */ - ereport(ERROR, - (errmsg("invalid statistics message length"))); - } - } - } - - /* - * EOF on the pipe implies that the buffer process exited. Fall - * out of outer loop. - */ - if (pipeEOF) - break; - - /* - * Distribute the message to the specific function handling it. - */ - switch (msg.msg_hdr.m_type) - { - case PGSTAT_MTYPE_DUMMY: - break; - - case PGSTAT_MTYPE_TABSTAT: - pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, nread); - break; - - case PGSTAT_MTYPE_TABPURGE: - pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, nread); - break; - - case PGSTAT_MTYPE_DROPDB: - pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, nread); - break; - - case PGSTAT_MTYPE_RESETCOUNTER: - pgstat_recv_resetcounter((PgStat_MsgResetcounter *) &msg, - nread); - break; - - case PGSTAT_MTYPE_AUTOVAC_START: - pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, nread); - break; - - case PGSTAT_MTYPE_VACUUM: - pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, nread); - break; - - case PGSTAT_MTYPE_ANALYZE: - pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, nread); - break; - - default: - break; - } - - /* - * If this is the first message after we wrote the stats file the - * last time, enable the alarm interrupt to make it be written - * again later. - */ - if (need_timer) - { - if (setitimer(ITIMER_REAL, &timeout, NULL)) - ereport(ERROR, - (errmsg("could not set statistics collector timer: %m"))); - need_timer = false; - } - } - - /* - * Note that we do NOT check for postmaster exit inside the loop; only - * EOF on the buffer pipe causes us to fall out. This ensures we - * don't exit prematurely if there are still a few messages in the - * buffer or pipe at postmaster shutdown. - */ - } - - /* - * Okay, we saw EOF on the buffer pipe, so there are no more messages to - * process. If the buffer process quit because of postmaster shutdown, we - * want to save the final stats to reuse at next startup. But if the - * buffer process failed, it seems best not to (there may even now be a - * new collector firing up, and we don't want it to read a - * partially-rewritten stats file). - */ - if (!PostmasterIsAlive(false)) - pgstat_write_statsfile(); -} - - -/* SIGALRM signal handler for collector process */ -static void -force_statwrite(SIGNAL_ARGS) -{ - need_statwrite = true; -} - - -/* ---------- - * pgstat_recvbuffer() - - * - * This is the body of the separate buffering process. Its only - * purpose is to receive messages from the UDP socket as fast as - * possible and forward them over a pipe into the collector itself. - * If the collector is slow to absorb messages, they are buffered here. - * ---------- - */ -static void -pgstat_recvbuffer(void) -{ - fd_set rfds; - fd_set wfds; - struct timeval timeout; - int writePipe = pgStatPipe[1]; - int maxfd; - int len; - int xfr; - int frm; - PgStat_Msg input_buffer; - char *msgbuffer; - int msg_send = 0; /* next send index in buffer */ - int msg_recv = 0; /* next receive index */ - int msg_have = 0; /* number of bytes stored */ - bool overflow = false; - - /* - * Identify myself via ps - */ - init_ps_display("stats buffer process", "", "", ""); - - /* - * We want to die if our child collector process does. There are two ways - * we might notice that it has died: receive SIGCHLD, or get a write - * failure on the pipe leading to the child. We can set SIGPIPE to kill - * us here. Our SIGCHLD handler was already set up before we forked (must - * do it that way, else it's a race condition). - */ - pqsignal(SIGPIPE, SIG_DFL); - PG_SETMASK(&UnBlockSig); - - /* - * Set the write pipe to nonblock mode, so that we cannot block when the - * collector falls behind. - */ - if (!pg_set_noblock(writePipe)) - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("could not set statistics collector pipe to nonblocking mode: %m"))); - - /* - * Allocate the message buffer - */ - msgbuffer = (char *) palloc(PGSTAT_RECVBUFFERSZ); - - /* - * Loop forever - */ - for (;;) - { - FD_ZERO(&rfds); - FD_ZERO(&wfds); - maxfd = -1; - - /* - * As long as we have buffer space we add the socket to the read - * descriptor set. - */ - if (msg_have <= (int) (PGSTAT_RECVBUFFERSZ - sizeof(PgStat_Msg))) - { - FD_SET(pgStatSock, &rfds); - maxfd = pgStatSock; - overflow = false; - } - else - { - if (!overflow) - { - ereport(LOG, - (errmsg("statistics buffer is full"))); - overflow = true; - } - } - - /* - * If we have messages to write out, we add the pipe to the write - * descriptor set. - */ - if (msg_have > 0) - { - FD_SET(writePipe, &wfds); - if (writePipe > maxfd) - maxfd = writePipe; - } - - /* - * 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.) - * - * struct timeout is modified by select() on some operating systems, - * so re-fill it each time. - */ - timeout.tv_sec = 10; - timeout.tv_usec = 0; - - if (select(maxfd + 1, &rfds, &wfds, NULL, &timeout) < 0) - { - if (errno == EINTR) - continue; - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("select() failed in statistics buffer: %m"))); - } +#endif /* HAVE_POLL */ /* * If there is a message on the socket, read it and check for * validity. */ - if (FD_ISSET(pgStatSock, &rfds)) + if (got_data) { - len = recv(pgStatSock, (char *) &input_buffer, + len = recv(pgStatSock, (char *) &msg, sizeof(PgStat_Msg), 0); if (len < 0) ereport(ERROR, @@ -2082,110 +1734,95 @@ pgstat_recvbuffer(void) /* * The received length must match the length in the header */ - if (input_buffer.msg_hdr.m_size != len) + if (msg.msg_hdr.m_size != len) continue; /* - * O.K. - we accept this message. Copy it to the circular - * msgbuffer. + * O.K. - we accept this message. Process it. */ - frm = 0; - while (len > 0) + switch (msg.msg_hdr.m_type) { - xfr = PGSTAT_RECVBUFFERSZ - msg_recv; - if (xfr > len) - xfr = len; - Assert(xfr > 0); - memcpy(msgbuffer + msg_recv, - ((char *) &input_buffer) + frm, - xfr); - msg_recv += xfr; - if (msg_recv == PGSTAT_RECVBUFFERSZ) - msg_recv = 0; - msg_have += xfr; - frm += xfr; - len -= xfr; + case PGSTAT_MTYPE_DUMMY: + break; + + case PGSTAT_MTYPE_TABSTAT: + pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, len); + break; + + case PGSTAT_MTYPE_TABPURGE: + pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, len); + break; + + case PGSTAT_MTYPE_DROPDB: + pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, len); + break; + + case PGSTAT_MTYPE_RESETCOUNTER: + pgstat_recv_resetcounter((PgStat_MsgResetcounter *) &msg, + len); + break; + + case PGSTAT_MTYPE_AUTOVAC_START: + pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, len); + break; + + case PGSTAT_MTYPE_VACUUM: + pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, len); + break; + + case PGSTAT_MTYPE_ANALYZE: + pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len); + break; + + default: + break; + } + + /* + * If this is the first message after we wrote the stats file the + * last time, enable the alarm interrupt to make it be written + * again later. + */ + if (need_timer) + { + if (setitimer(ITIMER_REAL, &write_timeout, NULL)) + ereport(ERROR, + (errmsg("could not set statistics collector timer: %m"))); + need_timer = false; } } - - /* - * If the collector is ready to receive, write some data into his - * pipe. We may or may not be able to write all that we have. - * - * NOTE: if what we have is less than PIPE_BUF bytes but more than the - * space available in the pipe buffer, most kernels will refuse to - * write any of it, and will return EAGAIN. This means we will - * busy-loop until the situation changes (either because the collector - * caught up, or because more data arrives so that we have more than - * PIPE_BUF bytes buffered). This is not good, but is there any way - * around it? We have no way to tell when the collector has caught - * up... - */ - if (FD_ISSET(writePipe, &wfds)) + else { - xfr = PGSTAT_RECVBUFFERSZ - msg_send; - if (xfr > msg_have) - xfr = msg_have; - Assert(xfr > 0); - len = pipewrite(writePipe, msgbuffer + msg_send, xfr); - if (len < 0) - { - if (errno == EINTR || errno == EAGAIN) - continue; /* not enough space in pipe */ - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("could not write to statistics collector pipe: %m"))); - } - /* NB: len < xfr is okay */ - msg_send += len; - if (msg_send == PGSTAT_RECVBUFFERSZ) - msg_send = 0; - msg_have -= len; + /* + * We can only get here if the select/poll timeout elapsed. + * Check for postmaster death. + */ + if (!PostmasterIsAlive(true)) + break; } - - /* - * Make sure we forwarded all messages before we check for postmaster - * termination. - */ - if (msg_have != 0 || FD_ISSET(pgStatSock, &rfds)) - continue; - - /* - * 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. - */ + } /* end of message-processing loop */ /* - * If running in bufferer, kill our collector as well. On some broken - * win32 systems, it does not shut down automatically because of issues - * with socket inheritance. XXX so why not fix the socket inheritance... + * Save the final stats to reuse at next startup. */ -#ifdef WIN32 - if (pgStatCollectorPid > 0) - kill(pgStatCollectorPid, SIGQUIT); -#endif + pgstat_write_statsfile(); + exit(0); } -/* SIGCHLD signal handler for buffer process */ + +/* SIGQUIT signal handler for collector process */ static void -pgstat_die(SIGNAL_ARGS) +pgstat_exit(SIGNAL_ARGS) { - exit(1); + need_exit = true; +} + +/* SIGALRM signal handler for collector process */ +static void +force_statwrite(SIGNAL_ARGS) +{ + need_statwrite = true; } diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index faf124587da..21f651085b5 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.489 2006/06/27 22:16:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.490 2006/06/29 20:00:08 tgl Exp $ * * NOTES * @@ -331,8 +331,6 @@ typedef struct PROC_HDR *ProcGlobal; PGPROC *DummyProcs; InheritableSocket pgStatSock; - InheritableSocket pgStatPipe0; - InheritableSocket pgStatPipe1; pid_t PostmasterPid; TimestampTz PgStartTime; #ifdef WIN32 @@ -2352,7 +2350,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) kill(PgArchPID, SIGQUIT); } - /* Force a power-cycle of the pgstat processes too */ + /* Force a power-cycle of the pgstat process too */ /* (Shouldn't be necessary, but just for luck) */ if (PgStatPID != 0 && !FatalError) { @@ -3324,25 +3322,13 @@ SubPostmasterMain(int argc, char *argv[]) PgArchiverMain(argc, argv); proc_exit(0); } - if (strcmp(argv[1], "--forkbuf") == 0) + if (strcmp(argv[1], "--forkcol") == 0) { /* Close the postmaster's sockets */ ClosePostmasterPorts(false); /* Do not want to attach to shared memory */ - PgstatBufferMain(argc, argv); - proc_exit(0); - } - if (strcmp(argv[1], "--forkcol") == 0) - { - /* - * Do NOT close postmaster sockets here, because we are forking from - * pgstat buffer process, which already did it. - */ - - /* Do not want to attach to shared memory */ - PgstatCollectorMain(argc, argv); proc_exit(0); } @@ -3679,7 +3665,6 @@ extern slock_t *ProcStructLock; extern PROC_HDR *ProcGlobal; extern PGPROC *DummyProcs; extern int pgStatSock; -extern int pgStatPipe[2]; #ifndef WIN32 #define write_inheritable_socket(dest, src, childpid) (*(dest) = (src)) @@ -3723,8 +3708,6 @@ save_backend_variables(BackendParameters * param, Port *port, param->ProcGlobal = ProcGlobal; param->DummyProcs = DummyProcs; write_inheritable_socket(¶m->pgStatSock, pgStatSock, childPid); - write_inheritable_socket(¶m->pgStatPipe0, pgStatPipe[0], childPid); - write_inheritable_socket(¶m->pgStatPipe1, pgStatPipe[1], childPid); param->PostmasterPid = PostmasterPid; param->PgStartTime = PgStartTime; @@ -3928,8 +3911,6 @@ restore_backend_variables(BackendParameters * param, Port *port) ProcGlobal = param->ProcGlobal; DummyProcs = param->DummyProcs; read_inheritable_socket(&pgStatSock, ¶m->pgStatSock); - read_inheritable_socket(&pgStatPipe[0], ¶m->pgStatPipe0); - read_inheritable_socket(&pgStatPipe[1], ¶m->pgStatPipe1); PostmasterPid = param->PostmasterPid; PgStartTime = param->PgStartTime; diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 83a45d7217f..72e542f16c8 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -5,7 +5,7 @@ * * Copyright (c) 2001-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.47 2006/06/19 01:51:21 tgl Exp $ + * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.48 2006/06/29 20:00:08 tgl Exp $ * ---------- */ #ifndef PGSTAT_H @@ -362,7 +362,6 @@ extern int pgstat_start(void); extern void pgstat_reset_all(void); #ifdef EXEC_BACKEND -extern void PgstatBufferMain(int argc, char *argv[]); extern void PgstatCollectorMain(int argc, char *argv[]); #endif