diff options
Diffstat (limited to 'src/backend/postmaster/pgstat.c')
-rw-r--r-- | src/backend/postmaster/pgstat.c | 1160 |
1 files changed, 465 insertions, 695 deletions
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 50486f8cef2..f6949f14377 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,52 +620,6 @@ 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 *------------------------------------------------------------ @@ -679,135 +627,6 @@ pgstat_report_autovac(Oid dboid) /* ---------- - * 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 "<IDLE>" 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() - * * Called from tcop/postgres.c to send the so far collected @@ -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 */ @@ -1086,6 +904,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() - * * Send some junk data to the collector to increase traffic. @@ -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,12 +1296,262 @@ pgstat_fetch_stat_beentry(int beid) int pgstat_fetch_stat_numbackends(void) { - backend_read_statsfile(); + pgstat_read_current_status(); - return pgStatNumBackends; + return localNumBackends; } +/* ------------------------------------------------------------ + * Functions for management of the shared-memory PgBackendStatus array + * ------------------------------------------------------------ + */ + +static PgBackendStatus *BackendStatusArray = NULL; +static PgBackendStatus *MyBEEntry = NULL; + + +/* + * Report shared-memory space needed by CreateSharedBackendStatus. + */ +Size +BackendStatusShmemSize(void) +{ + Size size; + + size = mul_size(sizeof(PgBackendStatus), MaxBackends); + return size; +} + +/* + * Initialize the shared status array during postmaster startup. + */ +void +CreateSharedBackendStatus(void) +{ + 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 "<IDLE>" 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 @@ -1425,8 +1569,6 @@ static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype) { hdr->m_type = mtype; - hdr->m_backendid = MyBackendId; - hdr->m_procpid = MyProcPid; } @@ -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; @@ -1773,11 +1897,6 @@ PgstatCollectorMain(int argc, char *argv[]) } /* - * 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 * again later. @@ -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; /* @@ -2271,29 +2297,6 @@ pgstat_write_statsfile(void) } /* - * 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 * after each individual fputc or fwrite above. @@ -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); } /* @@ -2407,15 +2383,6 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, 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 * with empty counters. @@ -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,111 +2508,6 @@ 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. - */ - if (use_mcxt == NULL) - *betab = (PgStat_StatBeEntry *) - palloc(sizeof(PgStat_StatBeEntry) * maxbackends); - else - *betab = (PgStat_StatBeEntry *) - MemoryContextAlloc(use_mcxt, - sizeof(PgStat_StatBeEntry) * maxbackends); - break; - - /* - * 'B' A PgStat_StatBeEntry follows. - */ - case 'B': - if (betab == NULL || numbackends == NULL || *betab == NULL) - 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': @@ -2667,7 +2528,7 @@ done: * 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); /* @@ -3014,12 +2691,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. */ dbentry = pgstat_get_db_entry(msg->m_databaseid, false); @@ -3055,12 +2726,6 @@ pgstat_recv_resetcounter(PgStat_MsgResetcounter *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. Nothing to do if not there. */ dbentry = pgstat_get_db_entry(msg->m_databaseid, false); @@ -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; +} |