From b13c9686d08411a284afeb881b49d7955761d8cb Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 19 Jun 2006 01:51:22 +0000 Subject: [PATCH] Take the statistics collector out of the loop for monitoring backends' current commands; instead, store current-status information in shared memory. This substantially reduces the overhead of stats_command_string and also ensures that pg_stat_activity is fully up to date at all times. Per my recent proposal. --- doc/src/sgml/config.sgml | 134 +- doc/src/sgml/monitoring.sgml | 59 +- src/backend/postmaster/pgstat.c | 1224 +++++++---------- src/backend/postmaster/postmaster.c | 9 +- src/backend/storage/ipc/ipci.c | 5 +- src/backend/storage/ipc/procarray.c | 38 +- src/backend/utils/adt/pgstatfuncs.c | 217 ++- src/backend/utils/misc/guc.c | 23 +- src/backend/utils/misc/postgresql.conf.sample | 16 +- src/include/pgstat.h | 266 ++-- src/include/storage/procarray.h | 3 +- 11 files changed, 838 insertions(+), 1156 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index ebfd6012fc..8f6ffa9a1a 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1,4 +1,4 @@ - + Server Configuration @@ -2845,43 +2845,6 @@ SELECT * FROM parent WHERE key = 2400; Run-Time Statistics - - Statistics Monitoring - - - - log_statement_stats (boolean) - log_parser_stats (boolean) - log_planner_stats (boolean) - log_executor_stats (boolean) - - log_statement_stats configuration parameter - - - log_parser_stats configuration parameter - - - log_planner_stats configuration parameter - - - log_executor_stats configuration parameter - - - - For each query, write performance statistics of the respective - module to the server log. This is a crude profiling - instrument. log_statement_stats reports total - statement statistics, while the others report per-module statistics. - log_statement_stats cannot be enabled together with - any of the per-module options. All of these options are disabled by - default. Only superusers can change these settings. - - - - - - - Query and Index Statistics Collector @@ -2893,8 +2856,38 @@ SELECT * FROM parent WHERE key = 2400; Refer to for more information. + + + As of PostgreSQL 8.2, + stats_command_string controls a separate data + collection mechanism that can be turned on or off independently + of whether the statistics-collection subprocess is running. + The subprocess is only needed to support collection of + block-level or row-level statistics. + + + + + stats_command_string (boolean) + + stats_command_string configuration parameter + + + + Enables the collection of information on the currently + executing command of each session, along with the time at + which that command began execution. This parameter is off by + default. Note that even when enabled, this information is not + visible to all users, only to superusers and the user owning + the session being reported on; so it should not represent a + security risk. + Only superusers can change this setting. + + + + stats_start_collector (boolean) @@ -2914,25 +2907,6 @@ SELECT * FROM parent WHERE key = 2400; - - stats_command_string (boolean) - - stats_command_string configuration parameter - - - - Enables the collection of statistics on the currently - executing command of each session, along with the time at - which that command began execution. This parameter is off by - default. Note that even when enabled, this information is not - visible to all users, only to superusers and the user owning - the session being reported on; so it should not represent a - security risk. - Only superusers can change this setting. - - - - stats_block_level (boolean) @@ -2968,15 +2942,53 @@ SELECT * FROM parent WHERE key = 2400; - If on, collected statistics are zeroed out whenever the server - is restarted. If off, statistics are accumulated across server - restarts. The default is off. This parameter can only - be set at server start. + If on, collected block-level and row-level statistics are zeroed out + whenever the server is restarted. If off, statistics are accumulated + across server restarts. This parameter is off by default. + This parameter can only be set at server start. + + + + + + + + + Statistics Monitoring + + + + log_statement_stats (boolean) + log_parser_stats (boolean) + log_planner_stats (boolean) + log_executor_stats (boolean) + + log_statement_stats configuration parameter + + + log_parser_stats configuration parameter + + + log_planner_stats configuration parameter + + + log_executor_stats configuration parameter + + + + For each query, write performance statistics of the respective + module to the server log. This is a crude profiling + instrument. log_statement_stats reports total + statement statistics, while the others report per-module statistics. + log_statement_stats cannot be enabled together with + any of the per-module options. All of these options are disabled by + default. Only superusers can change these settings. + diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 2eac524f80..54c6b4f973 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1,4 +1,4 @@ - + Monitoring Database Activity @@ -109,9 +109,14 @@ postgres: user database host PostgreSQL's statistics collector is a subsystem that supports collection and reporting of information about server activity. Presently, the collector can count accesses to tables - and indexes in both disk-block and individual-row terms. It also supports - determining the exact command currently being executed by other server - processes. + and indexes in both disk-block and individual-row terms. + + + + PostgreSQL also supports determining the exact + command currently being executed by other server processes. This is an + independent facility that can be enabled or disabled whether or not + block-level and row-level statistics are being collected. @@ -136,15 +141,21 @@ postgres: user database host - The parameters , - , and and control how much information is actually sent to the collector and thus determine how much run-time overhead occurs. These respectively determine whether a server - process sends its current command string, disk-block-level access - statistics, and row-level access statistics to the collector. + process tracks disk-block-level access + statistics and row-level access statistics and sends these to the collector. Additionally, per-database transaction commit and abort statistics - are collected if any of these parameters are set. + are collected if either of these parameters are set. + + + + The parameter enables monitoring + of the current command being executed by any server process. + The statistics collector subprocess need not be running to enable this + feature. @@ -165,7 +176,7 @@ postgres: user database host @@ -190,10 +201,9 @@ postgres: user database host PGSTAT_STAT_INTERVAL milliseconds (500 unless altered while building the server). So the - displayed information lags behind actual activity. Current-query - information is reported to the collector immediately, but is still subject - to the PGSTAT_STAT_INTERVAL delay before it becomes - visible. + displayed information lags behind actual activity. However, current-query + information collected by stats_command_string is + always up-to-date. @@ -202,7 +212,10 @@ postgres: user database host user database host stats_command_string has been turned on. Furthermore, these columns read as null unless the user examining the view is a superuser or the same as the user - owning the process being reported on. (Note that because of the - collector's reporting delay, the current query will only be - up-to-date for long-running queries.) + owning the process being reported on. + @@ -549,7 +561,7 @@ postgres: user database host pg_stat_get_last_vacuum_time(oid) timestamptz - Time of the last vacuum initiated by the user + Time of the last vacuum initiated by the user on this table @@ -557,7 +569,7 @@ postgres: user database host pg_stat_get_last_autovacuum_time(oid) timestamptz - Time of the last vacuum initiated by the autovacuum daemon + Time of the last vacuum initiated by the autovacuum daemon on this table @@ -565,7 +577,7 @@ postgres: user database host pg_stat_get_last_analyze_time(oid) timestamptz - Time of the last analyze initiated by the user + Time of the last analyze initiated by the user on this table @@ -573,7 +585,8 @@ postgres: user database host pg_stat_get_last_autoanalyze_time(oid) timestamptz - Time of the last analyze initiated by the autovacuum daemon + Time of the last analyze initiated by the autovacuum daemon on this + table @@ -677,7 +690,7 @@ postgres: user database host pg_stat_reset() boolean - Reset all currently collected statistics + Reset all block-level and row-level statistics to zero diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 50486f8cef..f6949f1437 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.128 2006/06/18 15:38:37 petere Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.129 2006/06/19 01:51:21 tgl Exp $ * ---------- */ #include "postgres.h" @@ -84,7 +84,6 @@ * ---------- */ #define PGSTAT_DB_HASH_SIZE 16 -#define PGSTAT_BE_HASH_SIZE 512 #define PGSTAT_TAB_HASH_SIZE 512 @@ -94,9 +93,9 @@ */ bool pgstat_collect_startcollector = true; bool pgstat_collect_resetonpmstart = false; -bool pgstat_collect_querystring = false; bool pgstat_collect_tuplelevel = false; bool pgstat_collect_blocklevel = false; +bool pgstat_collect_querystring = false; /* ---------- * Local data @@ -104,13 +103,13 @@ bool pgstat_collect_blocklevel = 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 long pgStatNumMessages = 0; - static bool pgStatRunningInCollector = false; /* @@ -135,8 +134,9 @@ static int pgStatXactRollback = 0; static TransactionId pgStatDBHashXact = InvalidTransactionId; static HTAB *pgStatDBHash = NULL; -static PgStat_StatBeEntry *pgStatBeTable = NULL; -static int pgStatNumBackends = 0; +static TransactionId pgStatLocalStatusXact = InvalidTransactionId; +static PgBackendStatus *localBackendStatusTable = NULL; +static int localNumBackends = 0; static volatile bool need_statwrite; @@ -166,21 +166,15 @@ static void pgstat_die(SIGNAL_ARGS); static void pgstat_beshutdown_hook(int code, Datum arg); static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create); -static int pgstat_add_backend(PgStat_MsgHdr *msg); -static void pgstat_sub_backend(int procpid); static void pgstat_drop_database(Oid databaseid); static void pgstat_write_statsfile(void); -static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, - PgStat_StatBeEntry **betab, - int *numbackends); +static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb); static void backend_read_statsfile(void); +static void pgstat_read_current_status(void); static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype); static void pgstat_send(void *msg, int len); -static void pgstat_recv_bestart(PgStat_MsgBestart *msg, int len); -static void pgstat_recv_beterm(PgStat_MsgBeterm *msg, int len); -static void pgstat_recv_activity(PgStat_MsgActivity *msg, int len); static void pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len); static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len); static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len); @@ -221,10 +215,11 @@ pgstat_init(void) #define TESTBYTEVAL ((char) 199) /* - * Force start of collector daemon if something to collect + * Force start of collector daemon if something to collect. Note that + * pgstat_collect_querystring is now an independent facility that does + * not require the collector daemon. */ - if (pgstat_collect_querystring || - pgstat_collect_tuplelevel || + if (pgstat_collect_tuplelevel || pgstat_collect_blocklevel) pgstat_collect_startcollector = true; @@ -451,7 +446,6 @@ startup_failed: /* Adjust GUC variables to suppress useless activity */ pgstat_collect_startcollector = false; - pgstat_collect_querystring = false; pgstat_collect_tuplelevel = false; pgstat_collect_blocklevel = false; } @@ -626,187 +620,12 @@ pgstat_start(void) } -/* ---------- - * pgstat_beterm() - - * - * Called from postmaster to tell collector a backend terminated. - * ---------- - */ -void -pgstat_beterm(int pid) -{ - PgStat_MsgBeterm msg; - - if (pgStatSock < 0) - return; - - /* can't use pgstat_setheader() because it's not called in a backend */ - MemSet(&(msg.m_hdr), 0, sizeof(msg.m_hdr)); - msg.m_hdr.m_type = PGSTAT_MTYPE_BETERM; - msg.m_hdr.m_procpid = pid; - - pgstat_send(&msg, sizeof(msg)); -} - - -/* ---------- - * pgstat_report_autovac() - - * - * Called from autovacuum.c to report startup of an autovacuum process. - * We are called before InitPostgres is done, so can't rely on MyDatabaseId; - * the db OID must be passed in, instead. - * ---------- - */ -void -pgstat_report_autovac(Oid dboid) -{ - PgStat_MsgAutovacStart msg; - - if (pgStatSock < 0) - return; - - pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START); - msg.m_databaseid = dboid; - msg.m_start_time = GetCurrentTimestamp(); - - pgstat_send(&msg, sizeof(msg)); -} - /* ------------------------------------------------------------ * Public functions used by backends follow *------------------------------------------------------------ */ -/* ---------- - * pgstat_bestart() - - * - * Tell the collector that this new backend is soon ready to process - * queries. Called from InitPostgres. - * ---------- - */ -void -pgstat_bestart(void) -{ - PgStat_MsgBestart msg; - - if (pgStatSock < 0) - return; - - /* - * We may not have a MyProcPort (eg, if this is the autovacuum process). - * Send an all-zeroes client address, which is dealt with specially in - * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port. - */ - pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_BESTART); - msg.m_databaseid = MyDatabaseId; - msg.m_userid = GetSessionUserId(); - if (MyProcPort) - memcpy(&msg.m_clientaddr, &MyProcPort->raddr, sizeof(msg.m_clientaddr)); - else - MemSet(&msg.m_clientaddr, 0, sizeof(msg.m_clientaddr)); - pgstat_send(&msg, sizeof(msg)); - - /* - * Set up a process-exit hook to ensure we flush the last batch of - * statistics to the collector. - */ - on_shmem_exit(pgstat_beshutdown_hook, 0); -} - -/* --------- - * pgstat_report_vacuum() - - * - * Tell the collector about the table we just vacuumed. - * --------- - */ -void -pgstat_report_vacuum(Oid tableoid, bool shared, - bool analyze, PgStat_Counter tuples) -{ - PgStat_MsgVacuum msg; - - if (pgStatSock < 0 || - !pgstat_collect_tuplelevel) - return; - - pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); - msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; - msg.m_tableoid = tableoid; - msg.m_analyze = analyze; - msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */ - msg.m_vacuumtime = GetCurrentTimestamp(); - msg.m_tuples = tuples; - pgstat_send(&msg, sizeof(msg)); -} - -/* -------- - * pgstat_report_analyze() - - * - * Tell the collector about the table we just analyzed. - * -------- - */ -void -pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, - PgStat_Counter deadtuples) -{ - PgStat_MsgAnalyze msg; - - if (pgStatSock < 0 || - !pgstat_collect_tuplelevel) - return; - - pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); - msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; - msg.m_tableoid = tableoid; - msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */ - msg.m_analyzetime = GetCurrentTimestamp(); - msg.m_live_tuples = livetuples; - msg.m_dead_tuples = deadtuples; - pgstat_send(&msg, sizeof(msg)); -} - -/* - * Flush any remaining statistics counts out to the collector at process - * exit. Without this, operations triggered during backend exit (such as - * temp table deletions) won't be counted. - */ -static void -pgstat_beshutdown_hook(int code, Datum arg) -{ - pgstat_report_tabstat(); -} - - -/* ---------- - * pgstat_report_activity() - - * - * Called from tcop/postgres.c to tell the collector what the backend - * is actually doing (usually "" or the start of the query to - * be executed). - * ---------- - */ -void -pgstat_report_activity(const char *cmd_str) -{ - PgStat_MsgActivity msg; - int len; - - if (!pgstat_collect_querystring || pgStatSock < 0) - return; - - len = strlen(cmd_str); - len = pg_mbcliplen(cmd_str, len, PGSTAT_ACTIVITY_SIZE - 1); - - memcpy(msg.m_cmd_str, cmd_str, len); - msg.m_cmd_str[len] = '\0'; - len += offsetof(PgStat_MsgActivity, m_cmd_str) + 1; - - pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ACTIVITY); - pgstat_send(&msg, len); -} - - /* ---------- * pgstat_report_tabstat() - * @@ -820,8 +639,7 @@ pgstat_report_tabstat(void) int i; if (pgStatSock < 0 || - (!pgstat_collect_querystring && - !pgstat_collect_tuplelevel && + (!pgstat_collect_tuplelevel && !pgstat_collect_blocklevel)) { /* Not reporting stats, so just flush whatever we have */ @@ -1085,6 +903,83 @@ pgstat_reset_counters(void) } +/* ---------- + * pgstat_report_autovac() - + * + * Called from autovacuum.c to report startup of an autovacuum process. + * We are called before InitPostgres is done, so can't rely on MyDatabaseId; + * the db OID must be passed in, instead. + * ---------- + */ +void +pgstat_report_autovac(Oid dboid) +{ + PgStat_MsgAutovacStart msg; + + if (pgStatSock < 0) + return; + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START); + msg.m_databaseid = dboid; + msg.m_start_time = GetCurrentTimestamp(); + + pgstat_send(&msg, sizeof(msg)); +} + + +/* --------- + * pgstat_report_vacuum() - + * + * Tell the collector about the table we just vacuumed. + * --------- + */ +void +pgstat_report_vacuum(Oid tableoid, bool shared, + bool analyze, PgStat_Counter tuples) +{ + PgStat_MsgVacuum msg; + + if (pgStatSock < 0 || + !pgstat_collect_tuplelevel) + return; + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); + msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; + msg.m_tableoid = tableoid; + msg.m_analyze = analyze; + msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */ + msg.m_vacuumtime = GetCurrentTimestamp(); + msg.m_tuples = tuples; + pgstat_send(&msg, sizeof(msg)); +} + +/* -------- + * pgstat_report_analyze() - + * + * Tell the collector about the table we just analyzed. + * -------- + */ +void +pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, + PgStat_Counter deadtuples) +{ + PgStat_MsgAnalyze msg; + + if (pgStatSock < 0 || + !pgstat_collect_tuplelevel) + return; + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); + msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; + msg.m_tableoid = tableoid; + msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */ + msg.m_analyzetime = GetCurrentTimestamp(); + msg.m_live_tuples = livetuples; + msg.m_dead_tuples = deadtuples; + pgstat_send(&msg, sizeof(msg)); +} + + /* ---------- * pgstat_ping() - * @@ -1231,8 +1126,7 @@ pgstat_initstats(PgStat_Info *stats, Relation rel) void pgstat_count_xact_commit(void) { - if (!pgstat_collect_querystring && - !pgstat_collect_tuplelevel && + if (!pgstat_collect_tuplelevel && !pgstat_collect_blocklevel) return; @@ -1263,8 +1157,7 @@ pgstat_count_xact_commit(void) void pgstat_count_xact_rollback(void) { - if (!pgstat_collect_querystring && - !pgstat_collect_tuplelevel && + if (!pgstat_collect_tuplelevel && !pgstat_collect_blocklevel) return; @@ -1375,20 +1268,21 @@ pgstat_fetch_stat_tabentry(Oid relid) * pgstat_fetch_stat_beentry() - * * Support function for the SQL-callable pgstat* functions. Returns - * the actual activity slot of one active backend. The caller is - * responsible for a check if the actual user is permitted to see - * that info (especially the querystring). + * our local copy of the current-activity entry for one backend. + * + * NB: caller is responsible for a check if the user is permitted to see + * this info (especially the querystring). * ---------- */ -PgStat_StatBeEntry * +PgBackendStatus * pgstat_fetch_stat_beentry(int beid) { - backend_read_statsfile(); + pgstat_read_current_status(); - if (beid < 1 || beid > pgStatNumBackends) + if (beid < 1 || beid > localNumBackends) return NULL; - return &pgStatBeTable[beid - 1]; + return &localBackendStatusTable[beid - 1]; } @@ -1402,45 +1296,293 @@ pgstat_fetch_stat_beentry(int beid) int pgstat_fetch_stat_numbackends(void) { - backend_read_statsfile(); + pgstat_read_current_status(); - return pgStatNumBackends; + return localNumBackends; } - /* ------------------------------------------------------------ - * Local support functions follow + * Functions for management of the shared-memory PgBackendStatus array * ------------------------------------------------------------ */ +static PgBackendStatus *BackendStatusArray = NULL; +static PgBackendStatus *MyBEEntry = NULL; -/* ---------- - * pgstat_setheader() - - * - * Set common header fields in a statistics message - * ---------- + +/* + * Report shared-memory space needed by CreateSharedBackendStatus. */ -static void -pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype) +Size +BackendStatusShmemSize(void) { - hdr->m_type = mtype; - hdr->m_backendid = MyBackendId; - hdr->m_procpid = MyProcPid; -} + Size size; + size = mul_size(sizeof(PgBackendStatus), MaxBackends); + return size; +} -/* ---------- - * pgstat_send() - - * - * Send out one statistics message to the collector - * ---------- +/* + * Initialize the shared status array during postmaster startup. */ -static void -pgstat_send(void *msg, int len) +void +CreateSharedBackendStatus(void) { - if (pgStatSock < 0) - return; + Size size = BackendStatusShmemSize(); + bool found; + + /* Create or attach to the shared array */ + BackendStatusArray = (PgBackendStatus *) + ShmemInitStruct("Backend Status Array", size, &found); + + if (!found) + { + /* + * We're the first - initialize. + */ + MemSet(BackendStatusArray, 0, size); + } +} + + +/* ---------- + * pgstat_bestart() - + * + * Initialize this backend's entry in the PgBackendStatus array, + * and set up an on-proc-exit hook that will clear it again. + * Called from InitPostgres. MyBackendId and MyDatabaseId must be set. + * ---------- + */ +void +pgstat_bestart(void) +{ + volatile PgBackendStatus *beentry; + TimestampTz proc_start_timestamp; + Oid userid; + SockAddr clientaddr; + + Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends); + MyBEEntry = &BackendStatusArray[MyBackendId - 1]; + + /* + * To minimize the time spent modifying the entry, fetch all the + * needed data first. + */ + proc_start_timestamp = GetCurrentTimestamp(); + userid = GetSessionUserId(); + + /* + * We may not have a MyProcPort (eg, if this is the autovacuum process). + * If so, use all-zeroes client address, which is dealt with specially in + * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port. + */ + if (MyProcPort) + memcpy(&clientaddr, &MyProcPort->raddr, sizeof(clientaddr)); + else + MemSet(&clientaddr, 0, sizeof(clientaddr)); + + /* + * Initialize my status entry, following the protocol of bumping + * st_changecount before and after; and make sure it's even afterwards. + * We use a volatile pointer here to ensure the compiler doesn't try to + * get cute. + */ + beentry = MyBEEntry; + do { + beentry->st_changecount++; + } while ((beentry->st_changecount & 1) == 0); + + beentry->st_procpid = MyProcPid; + beentry->st_proc_start_timestamp = proc_start_timestamp; + beentry->st_activity_start_timestamp = 0; + beentry->st_databaseid = MyDatabaseId; + beentry->st_userid = userid; + beentry->st_clientaddr = clientaddr; + beentry->st_activity[0] = '\0'; + /* Also make sure the last byte in the string area is always 0 */ + beentry->st_activity[PGBE_ACTIVITY_SIZE - 1] = '\0'; + + beentry->st_changecount++; + Assert((beentry->st_changecount & 1) == 0); + + /* + * Set up a process-exit hook to clean up. + */ + on_shmem_exit(pgstat_beshutdown_hook, 0); +} + +/* + * Shut down a single backend's statistics reporting at process exit. + * + * Flush any remaining statistics counts out to the collector. + * Without this, operations triggered during backend exit (such as + * temp table deletions) won't be counted. + * + * Lastly, clear out our entry in the PgBackendStatus array. + */ +static void +pgstat_beshutdown_hook(int code, Datum arg) +{ + volatile PgBackendStatus *beentry; + + pgstat_report_tabstat(); + + /* + * Clear my status entry, following the protocol of bumping + * st_changecount before and after. We use a volatile pointer here + * to ensure the compiler doesn't try to get cute. + */ + beentry = MyBEEntry; + beentry->st_changecount++; + + beentry->st_procpid = 0; /* mark invalid */ + + beentry->st_changecount++; + Assert((beentry->st_changecount & 1) == 0); +} + + +/* ---------- + * pgstat_report_activity() - + * + * Called from tcop/postgres.c to report what the backend is actually doing + * (usually "" or the start of the query to be executed). + * ---------- + */ +void +pgstat_report_activity(const char *cmd_str) +{ + volatile PgBackendStatus *beentry; + TimestampTz start_timestamp; + int len; + + if (!pgstat_collect_querystring) + return; + + /* + * To minimize the time spent modifying the entry, fetch all the + * needed data first. + */ + start_timestamp = GetCurrentTimestamp(); + + len = strlen(cmd_str); + len = pg_mbcliplen(cmd_str, len, PGBE_ACTIVITY_SIZE - 1); + + /* + * Update my status entry, following the protocol of bumping + * st_changecount before and after. We use a volatile pointer here + * to ensure the compiler doesn't try to get cute. + */ + beentry = MyBEEntry; + beentry->st_changecount++; + + beentry->st_activity_start_timestamp = start_timestamp; + memcpy((char *) beentry->st_activity, cmd_str, len); + beentry->st_activity[len] = '\0'; + + beentry->st_changecount++; + Assert((beentry->st_changecount & 1) == 0); +} + + +/* ---------- + * pgstat_read_current_status() - + * + * Copy the current contents of the PgBackendStatus array to local memory, + * if not already done in this transaction. + * ---------- + */ +static void +pgstat_read_current_status(void) +{ + TransactionId topXid = GetTopTransactionId(); + volatile PgBackendStatus *beentry; + PgBackendStatus *localentry; + int i; + + Assert(!pgStatRunningInCollector); + if (TransactionIdEquals(pgStatLocalStatusXact, topXid)) + return; /* already done */ + + localBackendStatusTable = (PgBackendStatus *) + MemoryContextAlloc(TopTransactionContext, + sizeof(PgBackendStatus) * MaxBackends); + localNumBackends = 0; + + beentry = BackendStatusArray; + localentry = localBackendStatusTable; + for (i = 1; i <= MaxBackends; i++) + { + /* + * Follow the protocol of retrying if st_changecount changes while + * we copy the entry, or if it's odd. (The check for odd is needed + * to cover the case where we are able to completely copy the entry + * while the source backend is between increment steps.) We use a + * volatile pointer here to ensure the compiler doesn't try to get + * cute. + */ + for (;;) + { + int save_changecount = beentry->st_changecount; + + /* + * XXX if PGBE_ACTIVITY_SIZE is really large, it might be best + * to use strcpy not memcpy for copying the activity string? + */ + memcpy(localentry, (char *) beentry, sizeof(PgBackendStatus)); + + if (save_changecount == beentry->st_changecount && + (save_changecount & 1) == 0) + break; + + /* Make sure we can break out of loop if stuck... */ + CHECK_FOR_INTERRUPTS(); + } + + beentry++; + /* Only valid entries get included into the local array */ + if (localentry->st_procpid > 0) + { + localentry++; + localNumBackends++; + } + } + + pgStatLocalStatusXact = topXid; +} + + +/* ------------------------------------------------------------ + * Local support functions follow + * ------------------------------------------------------------ + */ + + +/* ---------- + * pgstat_setheader() - + * + * Set common header fields in a statistics message + * ---------- + */ +static void +pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype) +{ + hdr->m_type = mtype; +} + + +/* ---------- + * pgstat_send() - + * + * Send out one statistics message to the collector + * ---------- + */ +static void +pgstat_send(void *msg, int len) +{ + if (pgStatSock < 0) + return; ((PgStat_MsgHdr *) msg)->m_size = len; @@ -1613,13 +1755,7 @@ PgstatCollectorMain(int argc, char *argv[]) * zero. */ pgStatRunningInCollector = true; - pgstat_read_statsfile(&pgStatDBHash, InvalidOid, NULL, NULL); - - /* - * Create the known backends table - */ - pgStatBeTable = (PgStat_StatBeEntry *) - palloc0(sizeof(PgStat_StatBeEntry) * MaxBackends); + pgstat_read_statsfile(&pgStatDBHash, InvalidOid); readPipe = pgStatPipe[0]; @@ -1727,14 +1863,6 @@ PgstatCollectorMain(int argc, char *argv[]) case PGSTAT_MTYPE_DUMMY: break; - case PGSTAT_MTYPE_BESTART: - pgstat_recv_bestart((PgStat_MsgBestart *) &msg, nread); - break; - - case PGSTAT_MTYPE_BETERM: - pgstat_recv_beterm((PgStat_MsgBeterm *) &msg, nread); - break; - case PGSTAT_MTYPE_TABSTAT: pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, nread); break; @@ -1743,10 +1871,6 @@ PgstatCollectorMain(int argc, char *argv[]) pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, nread); break; - case PGSTAT_MTYPE_ACTIVITY: - pgstat_recv_activity((PgStat_MsgActivity *) &msg, nread); - break; - case PGSTAT_MTYPE_DROPDB: pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, nread); break; @@ -1772,11 +1896,6 @@ PgstatCollectorMain(int argc, char *argv[]) break; } - /* - * Globally count messages. - */ - pgStatNumMessages++; - /* * If this is the first message after we wrote the stats file the * last time, enable the alarm interrupt to make it be written @@ -2066,66 +2185,6 @@ pgstat_die(SIGNAL_ARGS) } -/* ---------- - * pgstat_add_backend() - - * - * Support function to keep our backend list up to date. - * ---------- - */ -static int -pgstat_add_backend(PgStat_MsgHdr *msg) -{ - PgStat_StatBeEntry *beentry; - - /* - * Check that the backend ID is valid - */ - if (msg->m_backendid < 1 || msg->m_backendid > MaxBackends) - { - ereport(LOG, - (errmsg("invalid server process ID %d", msg->m_backendid))); - return -1; - } - - /* - * Get the slot for this backendid. - */ - beentry = &pgStatBeTable[msg->m_backendid - 1]; - - /* - * If the slot contains the PID of this backend, everything is fine and we - * have nothing to do. Note that all the slots are zero'd out when the - * collector is started. We assume that a slot is "empty" iff procpid == - * 0. - */ - if (beentry->procpid > 0 && beentry->procpid == msg->m_procpid) - return 0; - - /* Must be able to distinguish between empty and non-empty slots */ - Assert(msg->m_procpid > 0); - - /* - * Put this new backend into the slot (possibly overwriting an old entry, - * if we missed its BETERM or the BETERM hasn't arrived yet). - */ - beentry->procpid = msg->m_procpid; - beentry->start_timestamp = GetCurrentTimestamp(); - beentry->activity_start_timestamp = 0; - beentry->activity[0] = '\0'; - - /* - * We can't initialize the rest of the data in this slot until we see the - * BESTART message. Therefore, we set the database and user to sentinel - * values, to indicate "undefined". There is no easy way to do this for - * the client address, so make sure to check that the database or user are - * defined before accessing the client address. - */ - beentry->userid = InvalidOid; - beentry->databaseid = InvalidOid; - - return 0; -} - /* * Lookup the hash table entry for the specified database. If no hash * table entry exists, initialize it, if the create parameter is true. @@ -2171,38 +2230,6 @@ pgstat_get_db_entry(Oid databaseid, bool create) return result; } -/* ---------- - * pgstat_sub_backend() - - * - * Remove a backend from the actual backends list. - * ---------- - */ -static void -pgstat_sub_backend(int procpid) -{ - int i; - - /* - * Search in the known-backends table for the slot containing this PID. - */ - for (i = 0; i < MaxBackends; i++) - { - if (pgStatBeTable[i].procpid == procpid) - { - /* - * That's him. Mark the backend slot empty. - */ - pgStatBeTable[i].procpid = 0; - return; - } - } - - /* - * No big problem if not found. This can happen if UDP messages arrive out - * of order here. - */ -} - /* ---------- * pgstat_write_statsfile() - @@ -2218,7 +2245,6 @@ pgstat_write_statsfile(void) PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; FILE *fpout; - int i; int32 format_id; /* @@ -2270,29 +2296,6 @@ pgstat_write_statsfile(void) fputc('d', fpout); } - /* - * Write out the known running backends to the stats file. - */ - i = MaxBackends; - fputc('M', fpout); - fwrite(&i, sizeof(i), 1, fpout); - - for (i = 0; i < MaxBackends; i++) - { - PgStat_StatBeEntry *beentry = &pgStatBeTable[i]; - - if (beentry->procpid > 0) - { - int len; - - len = offsetof(PgStat_StatBeEntry, activity) + - strlen(beentry->activity) + 1; - fputc('B', fpout); - fwrite(&len, sizeof(len), 1, fpout); - fwrite(beentry, len, 1, fpout); - } - } - /* * No more output to be done. Close the temp file and replace the old * pgstat.stat with it. The ferror() check replaces testing for error @@ -2327,43 +2330,26 @@ pgstat_write_statsfile(void) } } -/* - * qsort/bsearch comparison routine for PIDs - * - * We assume PIDs are nonnegative, so there's no overflow risk - */ -static int -comparePids(const void *v1, const void *v2) -{ - return *((const int *) v1) - *((const int *) v2); -} /* ---------- * pgstat_read_statsfile() - * - * Reads in an existing statistics collector and initializes the - * databases' hash table (whose entries point to the tables' hash tables) - * and the current backend table. + * Reads in an existing statistics collector file and initializes the + * databases' hash table (whose entries point to the tables' hash tables). * ---------- */ static void -pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, - PgStat_StatBeEntry **betab, int *numbackends) +pgstat_read_statsfile(HTAB **dbhash, Oid onlydb) { PgStat_StatDBEntry *dbentry; PgStat_StatDBEntry dbbuf; PgStat_StatTabEntry *tabentry; PgStat_StatTabEntry tabbuf; - PgStat_StatBeEntry *beentry; HASHCTL hash_ctl; HTAB *tabhash = NULL; FILE *fpin; int32 format_id; - int len; - int maxbackends = 0; - int havebackends = 0; bool found; - int *live_pids; MemoryContext use_mcxt; int mcxt_flags; @@ -2373,26 +2359,16 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, * TopTransactionContext instead, so the caller must only know the last * XactId when this call happened to know if his tables are still valid or * already gone! - * - * Also, if running in a regular backend, we check backend entries against - * the PGPROC array so that we can detect stale entries. This lets us - * discard entries whose BETERM message got lost for some reason. */ if (pgStatRunningInCollector || IsAutoVacuumProcess()) { use_mcxt = NULL; mcxt_flags = 0; - live_pids = NULL; } else { use_mcxt = TopTransactionContext; mcxt_flags = HASH_CONTEXT; - live_pids = GetAllBackendPids(); - /* Sort the PID array so we can use bsearch */ - if (live_pids[0] > 1) - qsort((void *) &live_pids[1], live_pids[0], sizeof(int), - comparePids); } /* @@ -2406,15 +2382,6 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, *dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl, HASH_ELEM | HASH_FUNCTION | mcxt_flags); - /* - * Initialize the number of known backends to zero, just in case we do a - * silent error return below. - */ - if (numbackends != NULL) - *numbackends = 0; - if (betab != NULL) - *betab = NULL; - /* * Try to open the status file. If it doesn't exist, the backends simply * return zero for anything and the collector simply starts from scratch @@ -2472,7 +2439,6 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, memcpy(dbentry, &dbbuf, sizeof(PgStat_StatDBEntry)); dbentry->tables = NULL; - dbentry->n_backends = 0; /* * Don't collect tables if not the requested DB (or the @@ -2542,132 +2508,27 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, break; /* - * 'M' The maximum number of backends to expect follows. - */ - case 'M': - if (betab == NULL || numbackends == NULL) - goto done; - if (fread(&maxbackends, 1, sizeof(maxbackends), fpin) != - sizeof(maxbackends)) - { - ereport(pgStatRunningInCollector ? LOG : WARNING, - (errmsg("corrupted pgstat.stat file"))); - goto done; - } - if (maxbackends == 0) - goto done; - - /* - * Allocate space (in TopTransactionContext too) for the - * backend table. + * 'E' The EOF marker of a complete stats file. */ - if (use_mcxt == NULL) - *betab = (PgStat_StatBeEntry *) - palloc(sizeof(PgStat_StatBeEntry) * maxbackends); - else - *betab = (PgStat_StatBeEntry *) - MemoryContextAlloc(use_mcxt, - sizeof(PgStat_StatBeEntry) * maxbackends); - break; + case 'E': + goto done; - /* - * 'B' A PgStat_StatBeEntry follows. - */ - case 'B': - if (betab == NULL || numbackends == NULL || *betab == NULL) - goto done; + default: + ereport(pgStatRunningInCollector ? LOG : WARNING, + (errmsg("corrupted pgstat.stat file"))); + goto done; + } + } - if (havebackends >= maxbackends) - goto done; - - /* Read and validate the entry length */ - if (fread(&len, 1, sizeof(len), fpin) != sizeof(len)) - { - ereport(pgStatRunningInCollector ? LOG : WARNING, - (errmsg("corrupted pgstat.stat file"))); - goto done; - } - if (len <= offsetof(PgStat_StatBeEntry, activity) || - len > sizeof(PgStat_StatBeEntry)) - { - ereport(pgStatRunningInCollector ? LOG : WARNING, - (errmsg("corrupted pgstat.stat file"))); - goto done; - } - - /* - * Read it directly into the table. - */ - beentry = &(*betab)[havebackends]; - - if (fread(beentry, 1, len, fpin) != len) - { - ereport(pgStatRunningInCollector ? LOG : WARNING, - (errmsg("corrupted pgstat.stat file"))); - goto done; - } - - /* - * If possible, check PID to verify still running - */ - if (live_pids && - (live_pids[0] == 0 || - bsearch((void *) &beentry->procpid, - (void *) &live_pids[1], - live_pids[0], - sizeof(int), - comparePids) == NULL)) - { - /* - * Note: we could send a BETERM message to tell the - * collector to drop the entry, but I'm a bit worried - * about race conditions. For now, just silently ignore - * dead entries; they'll get recycled eventually anyway. - */ - - /* Don't accept the entry */ - memset(beentry, 0, sizeof(PgStat_StatBeEntry)); - break; - } - - /* - * Count backends per database here. - */ - dbentry = (PgStat_StatDBEntry *) - hash_search(*dbhash, - &(beentry->databaseid), - HASH_FIND, - NULL); - if (dbentry) - dbentry->n_backends++; - - havebackends++; - *numbackends = havebackends; - - break; - - /* - * 'E' The EOF marker of a complete stats file. - */ - case 'E': - goto done; - - default: - ereport(pgStatRunningInCollector ? LOG : WARNING, - (errmsg("corrupted pgstat.stat file"))); - goto done; - } - } - -done: - FreeFile(fpin); -} +done: + FreeFile(fpin); +} /* * If not done for this transaction, read the statistics collector * stats file into some hash tables. * - * Because we store the hash tables in TopTransactionContext, the result + * Because we store the tables in TopTransactionContext, the result * is good for the entire current main transaction. * * Inside the autovacuum process, the statfile is assumed to be valid @@ -2684,8 +2545,7 @@ backend_read_statsfile(void) if (pgStatDBHash) return; Assert(!pgStatRunningInCollector); - pgstat_read_statsfile(&pgStatDBHash, InvalidOid, - &pgStatBeTable, &pgStatNumBackends); + pgstat_read_statsfile(&pgStatDBHash, InvalidOid); } else { @@ -2694,183 +2554,12 @@ backend_read_statsfile(void) if (!TransactionIdEquals(pgStatDBHashXact, topXid)) { Assert(!pgStatRunningInCollector); - pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, - &pgStatBeTable, &pgStatNumBackends); + pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId); pgStatDBHashXact = topXid; } } } - -/* ---------- - * pgstat_recv_bestart() - - * - * Process a backend startup message. - * ---------- - */ -static void -pgstat_recv_bestart(PgStat_MsgBestart *msg, int len) -{ - PgStat_StatBeEntry *entry; - - /* - * If the backend is known dead, we ignore the message -- we don't want to - * update the backend entry's state since this BESTART message refers to - * an old, dead backend - */ - if (pgstat_add_backend(&msg->m_hdr) != 0) - return; - - entry = &(pgStatBeTable[msg->m_hdr.m_backendid - 1]); - entry->userid = msg->m_userid; - memcpy(&entry->clientaddr, &msg->m_clientaddr, sizeof(entry->clientaddr)); - entry->databaseid = msg->m_databaseid; -} - - -/* ---------- - * pgstat_recv_beterm() - - * - * Process a backend termination message. - * ---------- - */ -static void -pgstat_recv_beterm(PgStat_MsgBeterm *msg, int len) -{ - pgstat_sub_backend(msg->m_hdr.m_procpid); -} - -/* ---------- - * pgstat_recv_autovac() - - * - * Process an autovacuum signalling message. - * ---------- - */ -static void -pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len) -{ - PgStat_StatDBEntry *dbentry; - - /* - * Lookup the database in the hashtable. Don't create the entry if it - * doesn't exist, because autovacuum may be processing a template - * database. If this isn't the case, the database is most likely to have - * an entry already. (If it doesn't, not much harm is done anyway -- - * it'll get created as soon as somebody actually uses the database.) - */ - dbentry = pgstat_get_db_entry(msg->m_databaseid, false); - if (dbentry == NULL) - return; - - /* - * Store the last autovacuum time in the database entry. - */ - dbentry->last_autovac_time = msg->m_start_time; -} - -/* ---------- - * pgstat_recv_vacuum() - - * - * Process a VACUUM message. - * ---------- - */ -static void -pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len) -{ - PgStat_StatDBEntry *dbentry; - PgStat_StatTabEntry *tabentry; - - /* - * Don't create either the database or table entry if it doesn't already - * exist. This avoids bloating the stats with entries for stuff that is - * only touched by vacuum and not by live operations. - */ - dbentry = pgstat_get_db_entry(msg->m_databaseid, false); - if (dbentry == NULL) - return; - - tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), - HASH_FIND, NULL); - if (tabentry == NULL) - return; - - if (msg->m_autovacuum) - tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime; - else - tabentry->vacuum_timestamp = msg->m_vacuumtime; - tabentry->n_live_tuples = msg->m_tuples; - tabentry->n_dead_tuples = 0; - if (msg->m_analyze) - { - tabentry->last_anl_tuples = msg->m_tuples; - if (msg->m_autovacuum) - tabentry->autovac_analyze_timestamp = msg->m_vacuumtime; - else - tabentry->analyze_timestamp = msg->m_vacuumtime; - } -} - -/* ---------- - * pgstat_recv_analyze() - - * - * Process an ANALYZE message. - * ---------- - */ -static void -pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) -{ - PgStat_StatDBEntry *dbentry; - PgStat_StatTabEntry *tabentry; - - /* - * Don't create either the database or table entry if it doesn't already - * exist. This avoids bloating the stats with entries for stuff that is - * only touched by analyze and not by live operations. - */ - dbentry = pgstat_get_db_entry(msg->m_databaseid, false); - if (dbentry == NULL) - return; - - tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), - HASH_FIND, NULL); - if (tabentry == NULL) - return; - - if (msg->m_autovacuum) - tabentry->autovac_analyze_timestamp = msg->m_analyzetime; - else - tabentry->analyze_timestamp = msg->m_analyzetime; - tabentry->n_live_tuples = msg->m_live_tuples; - tabentry->n_dead_tuples = msg->m_dead_tuples; - tabentry->last_anl_tuples = msg->m_live_tuples + msg->m_dead_tuples; -} - -/* ---------- - * pgstat_recv_activity() - - * - * Remember what the backend is doing. - * ---------- - */ -static void -pgstat_recv_activity(PgStat_MsgActivity *msg, int len) -{ - PgStat_StatBeEntry *entry; - - /* - * Here we check explicitly for 0 return, since we don't want to mangle - * the activity of an active backend by a delayed packet from a dead one. - */ - if (pgstat_add_backend(&msg->m_hdr) != 0) - return; - - entry = &(pgStatBeTable[msg->m_hdr.m_backendid - 1]); - - StrNCpy(entry->activity, msg->m_cmd_str, PGSTAT_ACTIVITY_SIZE); - - entry->activity_start_timestamp = GetCurrentTimestamp(); -} - - /* ---------- * pgstat_recv_tabstat() - * @@ -2886,12 +2575,6 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len) int i; bool found; - /* - * Make sure the backend is counted for. - */ - if (pgstat_add_backend(&msg->m_hdr) < 0) - return; - dbentry = pgstat_get_db_entry(msg->m_databaseid, true); /* @@ -2975,12 +2658,6 @@ pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len) PgStat_StatDBEntry *dbentry; int i; - /* - * Make sure the backend is counted for. - */ - if (pgstat_add_backend(&msg->m_hdr) < 0) - return; - dbentry = pgstat_get_db_entry(msg->m_databaseid, false); /* @@ -3013,12 +2690,6 @@ pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len) { PgStat_StatDBEntry *dbentry; - /* - * Make sure the backend is counted for. - */ - if (pgstat_add_backend(&msg->m_hdr) < 0) - return; - /* * Lookup the database in the hashtable. */ @@ -3054,12 +2725,6 @@ pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len) HASHCTL hash_ctl; PgStat_StatDBEntry *dbentry; - /* - * Make sure the backend is counted for. - */ - if (pgstat_add_backend(&msg->m_hdr) < 0) - return; - /* * Lookup the database in the hashtable. Nothing to do if not there. */ @@ -3090,3 +2755,108 @@ pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len) &hash_ctl, HASH_ELEM | HASH_FUNCTION); } + +/* ---------- + * pgstat_recv_autovac() - + * + * Process an autovacuum signalling message. + * ---------- + */ +static void +pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len) +{ + PgStat_StatDBEntry *dbentry; + + /* + * Lookup the database in the hashtable. Don't create the entry if it + * doesn't exist, because autovacuum may be processing a template + * database. If this isn't the case, the database is most likely to have + * an entry already. (If it doesn't, not much harm is done anyway -- + * it'll get created as soon as somebody actually uses the database.) + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); + if (dbentry == NULL) + return; + + /* + * Store the last autovacuum time in the database entry. + */ + dbentry->last_autovac_time = msg->m_start_time; +} + +/* ---------- + * pgstat_recv_vacuum() - + * + * Process a VACUUM message. + * ---------- + */ +static void +pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len) +{ + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + + /* + * Don't create either the database or table entry if it doesn't already + * exist. This avoids bloating the stats with entries for stuff that is + * only touched by vacuum and not by live operations. + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); + if (dbentry == NULL) + return; + + tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), + HASH_FIND, NULL); + if (tabentry == NULL) + return; + + if (msg->m_autovacuum) + tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime; + else + tabentry->vacuum_timestamp = msg->m_vacuumtime; + tabentry->n_live_tuples = msg->m_tuples; + tabentry->n_dead_tuples = 0; + if (msg->m_analyze) + { + tabentry->last_anl_tuples = msg->m_tuples; + if (msg->m_autovacuum) + tabentry->autovac_analyze_timestamp = msg->m_vacuumtime; + else + tabentry->analyze_timestamp = msg->m_vacuumtime; + } +} + +/* ---------- + * pgstat_recv_analyze() - + * + * Process an ANALYZE message. + * ---------- + */ +static void +pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) +{ + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + + /* + * Don't create either the database or table entry if it doesn't already + * exist. This avoids bloating the stats with entries for stuff that is + * only touched by analyze and not by live operations. + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); + if (dbentry == NULL) + return; + + tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), + HASH_FIND, NULL); + if (tabentry == NULL) + return; + + if (msg->m_autovacuum) + tabentry->autovac_analyze_timestamp = msg->m_analyzetime; + else + tabentry->analyze_timestamp = msg->m_analyzetime; + tabentry->n_live_tuples = msg->m_live_tuples; + tabentry->n_dead_tuples = msg->m_dead_tuples; + tabentry->last_anl_tuples = msg->m_live_tuples + msg->m_dead_tuples; +} diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index fc4a9bcdc3..aac116b466 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.486 2006/06/18 15:38:37 petere Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.487 2006/06/19 01:51:21 tgl Exp $ * * NOTES * @@ -2109,9 +2109,6 @@ reaper(SIGNAL_ARGS) { AutoVacPID = 0; autovac_stopped(); - /* Tell the collector about process termination */ - pgstat_beterm(pid); - if (exitstatus != 0) HandleChildCrash(pid, exitstatus, _("autovacuum process")); @@ -2252,8 +2249,6 @@ CleanupBackend(int pid, #ifdef EXEC_BACKEND ShmemBackendArrayRemove(pid); #endif - /* Tell the collector about backend termination */ - pgstat_beterm(pid); break; } } @@ -2299,8 +2294,6 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) #ifdef EXEC_BACKEND ShmemBackendArrayRemove(pid); #endif - /* Tell the collector about backend termination */ - pgstat_beterm(pid); /* Keep looping so we can signal remaining backends */ } else diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 872a33ff16..4beba862de 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.83 2006/05/08 00:00:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.84 2006/06/19 01:51:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,7 @@ #include "access/twophase.h" #include "access/xlog.h" #include "miscadmin.h" +#include "pgstat.h" #include "postmaster/bgwriter.h" #include "postmaster/postmaster.h" #include "storage/bufmgr.h" @@ -86,6 +87,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) size = add_size(size, MultiXactShmemSize()); size = add_size(size, LWLockShmemSize()); size = add_size(size, ProcArrayShmemSize()); + size = add_size(size, BackendStatusShmemSize()); size = add_size(size, SInvalShmemSize()); size = add_size(size, FreeSpaceShmemSize()); size = add_size(size, BgWriterShmemSize()); @@ -167,6 +169,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) if (!IsUnderPostmaster) InitProcGlobal(); CreateSharedProcArray(); + CreateSharedBackendStatus(); /* * Set up shared-inval messaging diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 1cee032b52..f899b7a0e8 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -23,7 +23,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.11 2006/03/05 15:58:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.12 2006/06/19 01:51:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -732,42 +732,6 @@ IsBackendPid(int pid) return (BackendPidGetProc(pid) != NULL); } -/* - * GetAllBackendPids -- get an array of all current backends' PIDs - * - * The result is a palloc'd array with the number of active backends in - * entry [0], their PIDs in entries [1] .. [n]. The caller must bear in - * mind that the result may already be obsolete when returned. - */ -int * -GetAllBackendPids(void) -{ - int *result; - int npids; - ProcArrayStruct *arrayP = procArray; - int index; - - result = (int *) palloc((MaxBackends + 1) * sizeof(int)); - npids = 0; - - LWLockAcquire(ProcArrayLock, LW_SHARED); - - for (index = 0; index < arrayP->numProcs; index++) - { - PGPROC *proc = arrayP->procs[index]; - - if (proc->pid != 0) /* ignore dummy procs */ - result[++npids] = proc->pid; - } - - LWLockRelease(ProcArrayLock); - - Assert(npids <= MaxBackends); - - result[0] = npids; - return result; -} - /* * CountActiveBackends --- count backends (other than myself) that are in * active transactions. This is used as a heuristic to decide if diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index eaf379389e..dc03beec4d 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.29 2006/05/19 19:08:26 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.30 2006/06/19 01:51:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -61,11 +61,9 @@ extern Datum pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS); Datum pg_stat_get_numscans(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -79,11 +77,9 @@ pg_stat_get_numscans(PG_FUNCTION_ARGS) Datum pg_stat_get_tuples_returned(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -97,11 +93,9 @@ pg_stat_get_tuples_returned(PG_FUNCTION_ARGS) Datum pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -115,11 +109,9 @@ pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS) Datum pg_stat_get_tuples_inserted(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -133,11 +125,9 @@ pg_stat_get_tuples_inserted(PG_FUNCTION_ARGS) Datum pg_stat_get_tuples_updated(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -151,11 +141,9 @@ pg_stat_get_tuples_updated(PG_FUNCTION_ARGS) Datum pg_stat_get_tuples_deleted(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -169,11 +157,9 @@ pg_stat_get_tuples_deleted(PG_FUNCTION_ARGS) Datum pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -187,11 +173,9 @@ pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS) Datum pg_stat_get_blocks_hit(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); int64 result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -204,11 +188,9 @@ pg_stat_get_blocks_hit(PG_FUNCTION_ARGS) Datum pg_stat_get_last_vacuum_time(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); TimestampTz result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -224,11 +206,9 @@ pg_stat_get_last_vacuum_time(PG_FUNCTION_ARGS) Datum pg_stat_get_last_autovacuum_time(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); TimestampTz result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -244,11 +224,9 @@ pg_stat_get_last_autovacuum_time(PG_FUNCTION_ARGS) Datum pg_stat_get_last_analyze_time(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); TimestampTz result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -264,11 +242,9 @@ pg_stat_get_last_analyze_time(PG_FUNCTION_ARGS) Datum pg_stat_get_last_autoanalyze_time(PG_FUNCTION_ARGS) { - PgStat_StatTabEntry *tabentry; - Oid relid; + Oid relid = PG_GETARG_OID(0); TimestampTz result; - - relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) result = 0; @@ -342,75 +318,59 @@ pg_stat_reset(PG_FUNCTION_ARGS) Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS) { - PgStat_StatBeEntry *beentry; - int32 beid; - - beid = PG_GETARG_INT32(0); + int32 beid = PG_GETARG_INT32(0); + PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - PG_RETURN_INT32(beentry->procpid); + PG_RETURN_INT32(beentry->st_procpid); } Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS) { - PgStat_StatBeEntry *beentry; - int32 beid; - - beid = PG_GETARG_INT32(0); + int32 beid = PG_GETARG_INT32(0); + PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - /* Not initialized yet? */ - if (!OidIsValid(beentry->databaseid)) - PG_RETURN_NULL(); - - PG_RETURN_OID(beentry->databaseid); + PG_RETURN_OID(beentry->st_databaseid); } Datum pg_stat_get_backend_userid(PG_FUNCTION_ARGS) { - PgStat_StatBeEntry *beentry; - int32 beid; - - beid = PG_GETARG_INT32(0); + int32 beid = PG_GETARG_INT32(0); + PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - /* Not initialized yet? */ - if (!OidIsValid(beentry->userid)) - PG_RETURN_NULL(); - - PG_RETURN_OID(beentry->userid); + PG_RETURN_OID(beentry->st_userid); } Datum pg_stat_get_backend_activity(PG_FUNCTION_ARGS) { - PgStat_StatBeEntry *beentry; - int32 beid; - int len; - char *activity; + int32 beid = PG_GETARG_INT32(0); text *result; - - beid = PG_GETARG_INT32(0); + PgBackendStatus *beentry; + int len; + const char *activity; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) activity = ""; - else if (!superuser() && beentry->userid != GetUserId()) + else if (!superuser() && beentry->st_userid != GetUserId()) activity = ""; - else if (*(beentry->activity) == '\0') + else if (*(beentry->st_activity) == '\0') activity = ""; else - activity = beentry->activity; + activity = beentry->st_activity; len = strlen(activity); result = palloc(VARHDRSZ + len); @@ -426,15 +386,15 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); TimestampTz result; - PgStat_StatBeEntry *beentry; + PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->userid != GetUserId()) + if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); - result = beentry->activity_start_timestamp; + result = beentry->st_activity_start_timestamp; /* * No time recorded for start of current query -- this is the case if the @@ -451,15 +411,15 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); TimestampTz result; - PgStat_StatBeEntry *beentry; + PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->userid != GetUserId()) + if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); - result = beentry->start_timestamp; + result = beentry->st_proc_start_timestamp; if (result == 0) /* probably can't happen? */ PG_RETURN_NULL(); @@ -471,31 +431,25 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS) Datum pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS) { - PgStat_StatBeEntry *beentry; + int32 beid = PG_GETARG_INT32(0); + PgBackendStatus *beentry; SockAddr zero_clientaddr; - int32 beid; char remote_host[NI_MAXHOST]; int ret; - beid = PG_GETARG_INT32(0); - if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - /* Not initialized yet? */ - if (!OidIsValid(beentry->userid)) - PG_RETURN_NULL(); - - if (!superuser() && beentry->userid != GetUserId()) + if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); - if (memcmp(&(beentry->clientaddr), &zero_clientaddr, + if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr) == 0)) PG_RETURN_NULL(); - switch (beentry->clientaddr.addr.ss_family) + switch (beentry->st_clientaddr.addr.ss_family) { case AF_INET: #ifdef HAVE_IPV6 @@ -507,7 +461,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS) } remote_host[0] = '\0'; - ret = pg_getnameinfo_all(&beentry->clientaddr.addr, beentry->clientaddr.salen, + ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, + beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); @@ -521,31 +476,25 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS) Datum pg_stat_get_backend_client_port(PG_FUNCTION_ARGS) { - PgStat_StatBeEntry *beentry; + int32 beid = PG_GETARG_INT32(0); + PgBackendStatus *beentry; SockAddr zero_clientaddr; - int32 beid; char remote_port[NI_MAXSERV]; int ret; - beid = PG_GETARG_INT32(0); - if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - /* Not initialized yet? */ - if (!OidIsValid(beentry->userid)) - PG_RETURN_NULL(); - - if (!superuser() && beentry->userid != GetUserId()) + if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); - if (memcmp(&(beentry->clientaddr), &zero_clientaddr, + if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr) == 0)) PG_RETURN_NULL(); - switch (beentry->clientaddr.addr.ss_family) + switch (beentry->st_clientaddr.addr.ss_family) { case AF_INET: #ifdef HAVE_IPV6 @@ -559,31 +508,35 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS) } remote_port[0] = '\0'; - ret = pg_getnameinfo_all(&beentry->clientaddr.addr, - beentry->clientaddr.salen, + ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, + beentry->st_clientaddr.salen, NULL, 0, remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret) PG_RETURN_NULL(); - PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(remote_port))); + PG_RETURN_DATUM(DirectFunctionCall1(int4in, + CStringGetDatum(remote_port))); } Datum pg_stat_get_db_numbackends(PG_FUNCTION_ARGS) { - PgStat_StatDBEntry *dbentry; - Oid dbid; + Oid dbid = PG_GETARG_OID(0); int32 result; + int tot_backends = pgstat_fetch_stat_numbackends(); + int beid; - dbid = PG_GETARG_OID(0); + result = 0; + for (beid = 1; beid <= tot_backends; beid++) + { + PgBackendStatus *beentry = pgstat_fetch_stat_beentry(beid); - if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL) - result = 0; - else - result = (int32) (dbentry->n_backends); + if (beentry && beentry->st_databaseid == dbid) + result++; + } PG_RETURN_INT32(result); } @@ -592,11 +545,9 @@ pg_stat_get_db_numbackends(PG_FUNCTION_ARGS) Datum pg_stat_get_db_xact_commit(PG_FUNCTION_ARGS) { - PgStat_StatDBEntry *dbentry; - Oid dbid; + Oid dbid = PG_GETARG_OID(0); int64 result; - - dbid = PG_GETARG_OID(0); + PgStat_StatDBEntry *dbentry; if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL) result = 0; @@ -610,11 +561,9 @@ pg_stat_get_db_xact_commit(PG_FUNCTION_ARGS) Datum pg_stat_get_db_xact_rollback(PG_FUNCTION_ARGS) { - PgStat_StatDBEntry *dbentry; - Oid dbid; + Oid dbid = PG_GETARG_OID(0); int64 result; - - dbid = PG_GETARG_OID(0); + PgStat_StatDBEntry *dbentry; if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL) result = 0; @@ -628,11 +577,9 @@ pg_stat_get_db_xact_rollback(PG_FUNCTION_ARGS) Datum pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS) { - PgStat_StatDBEntry *dbentry; - Oid dbid; + Oid dbid = PG_GETARG_OID(0); int64 result; - - dbid = PG_GETARG_OID(0); + PgStat_StatDBEntry *dbentry; if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL) result = 0; @@ -646,11 +593,9 @@ pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS) Datum pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS) { - PgStat_StatDBEntry *dbentry; - Oid dbid; + Oid dbid = PG_GETARG_OID(0); int64 result; - - dbid = PG_GETARG_OID(0); + PgStat_StatDBEntry *dbentry; if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL) result = 0; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 9ef561d4c1..8c3931c838 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.321 2006/06/05 02:49:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.322 2006/06/19 01:51:21 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -700,16 +700,6 @@ static struct config_bool ConfigureNamesBool[] = &pgstat_collect_resetonpmstart, false, NULL, NULL }, - { - {"stats_command_string", PGC_SUSET, STATS_COLLECTOR, - gettext_noop("Collects statistics about executing commands."), - gettext_noop("Enables the collection of statistics on the currently " - "executing command of each session, along with the time " - "at which that command began execution.") - }, - &pgstat_collect_querystring, - false, NULL, NULL - }, { {"stats_row_level", PGC_SUSET, STATS_COLLECTOR, gettext_noop("Collects row-level statistics on database activity."), @@ -727,6 +717,17 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, + { + {"stats_command_string", PGC_SUSET, STATS_COLLECTOR, + gettext_noop("Collects information about executing commands."), + gettext_noop("Enables the collection of information on the currently " + "executing command of each session, along with the time " + "at which that command began execution.") + }, + &pgstat_collect_querystring, + false, NULL, NULL + }, + { {"autovacuum", PGC_SIGHUP, AUTOVACUUM, gettext_noop("Starts the autovacuum subprocess."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index ee5adaa2ff..26b2be6d6e 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -320,21 +320,21 @@ # RUNTIME STATISTICS #--------------------------------------------------------------------------- -# - Statistics Monitoring - - -#log_parser_stats = off -#log_planner_stats = off -#log_executor_stats = off -#log_statement_stats = off - # - Query/Index Statistics Collector - -#stats_start_collector = on #stats_command_string = off +#stats_start_collector = on # needed for block or row stats #stats_block_level = off #stats_row_level = off #stats_reset_on_server_start = off +# - Statistics Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + #--------------------------------------------------------------------------- # AUTOVACUUM PARAMETERS diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 0e4094bf3e..83a45d7217 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.46 2006/05/30 02:35:39 momjian Exp $ + * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.47 2006/06/19 01:51:21 tgl Exp $ * ---------- */ #ifndef PGSTAT_H @@ -16,16 +16,14 @@ #include "utils/rel.h" #include "utils/timestamp.h" + /* ---------- - * The types of backend/postmaster -> collector messages + * The types of backend -> collector messages * ---------- */ typedef enum StatMsgType { PGSTAT_MTYPE_DUMMY, - PGSTAT_MTYPE_BESTART, - PGSTAT_MTYPE_BETERM, - PGSTAT_MTYPE_ACTIVITY, PGSTAT_MTYPE_TABSTAT, PGSTAT_MTYPE_TABPURGE, PGSTAT_MTYPE_DROPDB, @@ -56,8 +54,6 @@ typedef struct PgStat_MsgHdr { StatMsgType m_type; int m_size; - int m_backendid; - int m_procpid; } PgStat_MsgHdr; /* ---------- @@ -68,6 +64,17 @@ typedef struct PgStat_MsgHdr */ #define PGSTAT_MSG_PAYLOAD (1000 - sizeof(PgStat_MsgHdr)) + +/* ---------- + * PgStat_MsgDummy A dummy message, ignored by the collector + * ---------- + */ +typedef struct PgStat_MsgDummy +{ + PgStat_MsgHdr m_hdr; +} PgStat_MsgDummy; + + /* ---------- * PgStat_TableEntry Per-table info in a MsgTabstat * @@ -96,37 +103,67 @@ typedef struct PgStat_TableEntry PgStat_Counter t_blocks_hit; } PgStat_TableEntry; +/* ---------- + * PgStat_MsgTabstat Sent by the backend to report table + * and buffer access statistics. + * ---------- + */ +#define PGSTAT_NUM_TABENTRIES \ + ((PGSTAT_MSG_PAYLOAD - sizeof(Oid) - 3 * sizeof(int)) \ + / sizeof(PgStat_TableEntry)) + +typedef struct PgStat_MsgTabstat +{ + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + int m_nentries; + int m_xact_commit; + int m_xact_rollback; + PgStat_TableEntry m_entry[PGSTAT_NUM_TABENTRIES]; +} PgStat_MsgTabstat; + /* ---------- - * PgStat_MsgDummy A dummy message, ignored by the collector + * PgStat_MsgTabpurge Sent by the backend to tell the collector + * about dead tables. * ---------- */ -typedef struct PgStat_MsgDummy +#define PGSTAT_NUM_TABPURGE \ + ((PGSTAT_MSG_PAYLOAD - sizeof(Oid) - sizeof(int)) \ + / sizeof(Oid)) + +typedef struct PgStat_MsgTabpurge { PgStat_MsgHdr m_hdr; - char m_dummy[512]; -} PgStat_MsgDummy; + Oid m_databaseid; + int m_nentries; + Oid m_tableid[PGSTAT_NUM_TABPURGE]; +} PgStat_MsgTabpurge; + /* ---------- - * PgStat_MsgBestart Sent by the backend on startup + * PgStat_MsgDropdb Sent by the backend to tell the collector + * about a dropped database * ---------- */ -typedef struct PgStat_MsgBestart +typedef struct PgStat_MsgDropdb { PgStat_MsgHdr m_hdr; Oid m_databaseid; - Oid m_userid; - SockAddr m_clientaddr; -} PgStat_MsgBestart; +} PgStat_MsgDropdb; + /* ---------- - * PgStat_MsgBeterm Sent by the postmaster after backend exit + * PgStat_MsgResetcounter Sent by the backend to tell the collector + * to reset counters * ---------- */ -typedef struct PgStat_MsgBeterm +typedef struct PgStat_MsgResetcounter { PgStat_MsgHdr m_hdr; -} PgStat_MsgBeterm; + Oid m_databaseid; +} PgStat_MsgResetcounter; + /* ---------- * PgStat_MsgAutovacStart Sent by the autovacuum daemon to signal @@ -140,6 +177,7 @@ typedef struct PgStat_MsgAutovacStart TimestampTz m_start_time; } PgStat_MsgAutovacStart; + /* ---------- * PgStat_MsgVacuum Sent by the backend or autovacuum daemon * after VACUUM or VACUUM ANALYZE @@ -156,6 +194,7 @@ typedef struct PgStat_MsgVacuum PgStat_Counter m_tuples; } PgStat_MsgVacuum; + /* ---------- * PgStat_MsgAnalyze Sent by the backend or autovacuum daemon * after ANALYZE @@ -173,80 +212,6 @@ typedef struct PgStat_MsgAnalyze } PgStat_MsgAnalyze; -/* ---------- - * PgStat_MsgActivity Sent by the backends when they start - * to parse a query. - * ---------- - */ -#define PGSTAT_ACTIVITY_SIZE PGSTAT_MSG_PAYLOAD - -typedef struct PgStat_MsgActivity -{ - PgStat_MsgHdr m_hdr; - char m_cmd_str[PGSTAT_ACTIVITY_SIZE]; -} PgStat_MsgActivity; - -/* ---------- - * PgStat_MsgTabstat Sent by the backend to report table - * and buffer access statistics. - * ---------- - */ -#define PGSTAT_NUM_TABENTRIES \ - ((PGSTAT_MSG_PAYLOAD - sizeof(Oid) - 3 * sizeof(int)) \ - / sizeof(PgStat_TableEntry)) - -typedef struct PgStat_MsgTabstat -{ - PgStat_MsgHdr m_hdr; - Oid m_databaseid; - int m_nentries; - int m_xact_commit; - int m_xact_rollback; - PgStat_TableEntry m_entry[PGSTAT_NUM_TABENTRIES]; -} PgStat_MsgTabstat; - -/* ---------- - * PgStat_MsgTabpurge Sent by the backend to tell the collector - * about dead tables. - * ---------- - */ -#define PGSTAT_NUM_TABPURGE \ - ((PGSTAT_MSG_PAYLOAD - sizeof(Oid) - sizeof(int)) \ - / sizeof(Oid)) - -typedef struct PgStat_MsgTabpurge -{ - PgStat_MsgHdr m_hdr; - Oid m_databaseid; - int m_nentries; - Oid m_tableid[PGSTAT_NUM_TABPURGE]; -} PgStat_MsgTabpurge; - - -/* ---------- - * PgStat_MsgDropdb Sent by the backend to tell the collector - * about a dropped database - * ---------- - */ -typedef struct PgStat_MsgDropdb -{ - PgStat_MsgHdr m_hdr; - Oid m_databaseid; -} PgStat_MsgDropdb; - - -/* ---------- - * PgStat_MsgResetcounter Sent by the backend to tell the collector - * to reset counters - * ---------- - */ -typedef struct PgStat_MsgResetcounter -{ - PgStat_MsgHdr m_hdr; - Oid m_databaseid; -} PgStat_MsgResetcounter; - - /* ---------- * PgStat_Msg Union over all possible messages. * ---------- @@ -255,8 +220,6 @@ typedef union PgStat_Msg { PgStat_MsgHdr msg_hdr; PgStat_MsgDummy msg_dummy; - PgStat_MsgBestart msg_bestart; - PgStat_MsgActivity msg_activity; PgStat_MsgTabstat msg_tabstat; PgStat_MsgTabpurge msg_tabpurge; PgStat_MsgDropdb msg_dropdb; @@ -275,19 +238,15 @@ typedef union PgStat_Msg * ------------------------------------------------------------ */ -#define PGSTAT_FILE_FORMAT_ID 0x01A5BC95 +#define PGSTAT_FILE_FORMAT_ID 0x01A5BC96 /* ---------- * PgStat_StatDBEntry The collector's data per database - * - * Note: n_backends is not maintained within the collector. It's computed - * when a backend reads the stats file for use. * ---------- */ typedef struct PgStat_StatDBEntry { Oid databaseid; - int n_backends; PgStat_Counter n_xact_commit; PgStat_Counter n_xact_rollback; PgStat_Counter n_blocks_fetched; @@ -302,35 +261,6 @@ typedef struct PgStat_StatDBEntry } PgStat_StatDBEntry; -/* ---------- - * PgStat_StatBeEntry The collector's data per backend - * ---------- - */ -typedef struct PgStat_StatBeEntry -{ - /* An entry is non-empty iff procpid > 0 */ - int procpid; - TimestampTz start_timestamp; - TimestampTz activity_start_timestamp; - - /* - * These fields are initialized by the BESTART message. If we have - * received messages from a backend before we have received its BESTART, - * these fields will be uninitialized: userid and databaseid will be - * InvalidOid, and clientaddr will be undefined. - */ - Oid userid; - Oid databaseid; - SockAddr clientaddr; - - /* - * activity[] must be last in the struct, because we only write as much - * of it as needed to the stats file. - */ - char activity[PGSTAT_ACTIVITY_SIZE]; -} PgStat_StatBeEntry; - - /* ---------- * PgStat_StatTabEntry The collector's data per table (or index) * ---------- @@ -338,10 +268,6 @@ typedef struct PgStat_StatBeEntry typedef struct PgStat_StatTabEntry { Oid tableid; - TimestampTz vacuum_timestamp; /* user initiated vacuum */ - TimestampTz autovac_vacuum_timestamp; /* autovacuum initiated */ - TimestampTz analyze_timestamp; /* user initiated */ - TimestampTz autovac_analyze_timestamp; /* autovacuum initiated */ PgStat_Counter numscans; @@ -358,27 +284,81 @@ typedef struct PgStat_StatTabEntry PgStat_Counter blocks_fetched; PgStat_Counter blocks_hit; + + TimestampTz vacuum_timestamp; /* user initiated vacuum */ + TimestampTz autovac_vacuum_timestamp; /* autovacuum initiated */ + TimestampTz analyze_timestamp; /* user initiated */ + TimestampTz autovac_analyze_timestamp; /* autovacuum initiated */ } PgStat_StatTabEntry; +/* ---------- + * Shared-memory data structures + * ---------- + */ + +/* Max length of st_activity string ... perhaps replace with a GUC var? */ +#define PGBE_ACTIVITY_SIZE 1024 + +/* ---------- + * PgBackendStatus + * + * Each live backend maintains a PgBackendStatus struct in shared memory + * showing its current activity. (The structs are allocated according to + * BackendId, but that is not critical.) Note that the collector process + * has no involvement in, or even access to, these structs. + * ---------- + */ +typedef struct PgBackendStatus +{ + /* + * To avoid locking overhead, we use the following protocol: a backend + * increments st_changecount before modifying its entry, and again after + * finishing a modification. A would-be reader should note the value + * of st_changecount, copy the entry into private memory, then check + * st_changecount again. If the value hasn't changed, and if it's even, + * the copy is valid; otherwise start over. This makes updates cheap + * while reads are potentially expensive, but that's the tradeoff we want. + */ + int st_changecount; + + /* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */ + int st_procpid; + + /* Times of backend process start and current activity start */ + TimestampTz st_proc_start_timestamp; + TimestampTz st_activity_start_timestamp; + + /* Database OID, owning user's OID, connection client address */ + Oid st_databaseid; + Oid st_userid; + SockAddr st_clientaddr; + + /* current command string; MUST be null-terminated */ + char st_activity[PGBE_ACTIVITY_SIZE]; +} PgBackendStatus; + + /* ---------- * GUC parameters * ---------- */ extern bool pgstat_collect_startcollector; extern bool pgstat_collect_resetonpmstart; -extern bool pgstat_collect_querystring; extern bool pgstat_collect_tuplelevel; extern bool pgstat_collect_blocklevel; +extern bool pgstat_collect_querystring; /* ---------- * Functions called from postmaster * ---------- */ +extern Size BackendStatusShmemSize(void); +extern void CreateSharedBackendStatus(void); + extern void pgstat_init(void); extern int pgstat_start(void); -extern void pgstat_beterm(int pid); extern void pgstat_reset_all(void); #ifdef EXEC_BACKEND @@ -391,21 +371,23 @@ extern void PgstatCollectorMain(int argc, char *argv[]); * Functions called from backends * ---------- */ -extern void pgstat_bestart(void); - extern void pgstat_ping(void); -extern void pgstat_report_activity(const char *what); + extern void pgstat_report_tabstat(void); +extern void pgstat_vacuum_tabstat(void); +extern void pgstat_drop_relation(Oid relid); + +extern void pgstat_reset_counters(void); + extern void pgstat_report_autovac(Oid dboid); extern void pgstat_report_vacuum(Oid tableoid, bool shared, bool analyze, PgStat_Counter tuples); extern void pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples); -extern void pgstat_vacuum_tabstat(void); -extern void pgstat_drop_relation(Oid relid); -extern void pgstat_reset_counters(void); +extern void pgstat_bestart(void); +extern void pgstat_report_activity(const char *what); extern void pgstat_initstats(PgStat_Info *stats, Relation rel); @@ -492,7 +474,7 @@ extern void pgstat_count_xact_rollback(void); */ extern PgStat_StatDBEntry *pgstat_fetch_stat_dbentry(Oid dbid); extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid); -extern PgStat_StatBeEntry *pgstat_fetch_stat_beentry(int beid); +extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid); extern int pgstat_fetch_stat_numbackends(void); #endif /* PGSTAT_H */ diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index a7da09a07b..e1710701a1 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.8 2006/03/05 15:59:00 momjian Exp $ + * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.9 2006/06/19 01:51:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,6 @@ extern TransactionId GetOldestXmin(bool allDbs); extern PGPROC *BackendPidGetProc(int pid); extern int BackendXidGetPid(TransactionId xid); extern bool IsBackendPid(int pid); -extern int *GetAllBackendPids(void); extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself); extern int CountActiveBackends(void); -- 2.40.0