diff options
Diffstat (limited to 'src/backend/postmaster/pgstat.c')
-rw-r--r-- | src/backend/postmaster/pgstat.c | 256 |
1 files changed, 174 insertions, 82 deletions
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index e2cc3508b83..4bb0fc60e3f 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -13,7 +13,7 @@ * * Copyright (c) 2001-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.101 2005/07/24 00:33:28 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.102 2005/07/29 19:30:04 tgl Exp $ * ---------- */ #include "postgres.h" @@ -119,12 +119,23 @@ static long pgStatNumMessages = 0; static bool pgStatRunningInCollector = FALSE; -static int pgStatTabstatAlloc = 0; -static int pgStatTabstatUsed = 0; -static PgStat_MsgTabstat **pgStatTabstatMessages = NULL; +/* + * Place where backends store per-table info to be sent to the collector. + * We store shared relations separately from non-shared ones, to be able to + * send them in separate messages. + */ +typedef struct TabStatArray +{ + int tsa_alloc; /* num allocated */ + int tsa_used; /* num actually used */ + PgStat_MsgTabstat **tsa_messages; /* the array itself */ +} TabStatArray; #define TABSTAT_QUANTUM 4 /* we alloc this many at a time */ +static TabStatArray RegularTabStat = { 0, 0, NULL }; +static TabStatArray SharedTabStat = { 0, 0, NULL }; + static int pgStatXactCommit = 0; static int pgStatXactRollback = 0; @@ -158,7 +169,7 @@ static void pgstat_exit(SIGNAL_ARGS); static void pgstat_die(SIGNAL_ARGS); static void pgstat_beshutdown_hook(int code, Datum arg); -static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid); +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); @@ -614,6 +625,7 @@ pgstat_beterm(int pid) 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; @@ -684,7 +696,8 @@ pgstat_bestart(void) * --------- */ void -pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples) +pgstat_report_vacuum(Oid tableoid, bool shared, + bool analyze, PgStat_Counter tuples) { PgStat_MsgVacuum msg; @@ -692,7 +705,7 @@ pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); - msg.m_databaseid = MyDatabaseId; + msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_analyze = analyze; msg.m_tuples = tuples; @@ -706,7 +719,7 @@ pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples) * -------- */ void -pgstat_report_analyze(Oid tableoid, PgStat_Counter livetuples, +pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples) { PgStat_MsgAnalyze msg; @@ -715,7 +728,7 @@ pgstat_report_analyze(Oid tableoid, PgStat_Counter livetuples, return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); - msg.m_databaseid = MyDatabaseId; + msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_live_tuples = livetuples; msg.m_dead_tuples = deadtuples; @@ -784,7 +797,8 @@ pgstat_report_tabstat(void) pgstat_collect_blocklevel)) { /* Not reporting stats, so just flush whatever we have */ - pgStatTabstatUsed = 0; + RegularTabStat.tsa_used = 0; + SharedTabStat.tsa_used = 0; return; } @@ -792,9 +806,9 @@ pgstat_report_tabstat(void) * For each message buffer used during the last query set the header * fields and send it out. */ - for (i = 0; i < pgStatTabstatUsed; i++) + for (i = 0; i < RegularTabStat.tsa_used; i++) { - PgStat_MsgTabstat *tsmsg = pgStatTabstatMessages[i]; + PgStat_MsgTabstat *tsmsg = RegularTabStat.tsa_messages[i]; int n; int len; @@ -811,8 +825,28 @@ pgstat_report_tabstat(void) tsmsg->m_databaseid = MyDatabaseId; pgstat_send(tsmsg, len); } + RegularTabStat.tsa_used = 0; + + /* Ditto, for shared relations */ + for (i = 0; i < SharedTabStat.tsa_used; i++) + { + PgStat_MsgTabstat *tsmsg = SharedTabStat.tsa_messages[i]; + int n; + int len; + + n = tsmsg->m_nentries; + len = offsetof(PgStat_MsgTabstat, m_entry[0]) + + n * sizeof(PgStat_TableEntry); - pgStatTabstatUsed = 0; + /* We don't report transaction commit/abort here */ + tsmsg->m_xact_commit = 0; + tsmsg->m_xact_rollback = 0; + + pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT); + tsmsg->m_databaseid = InvalidOid; + pgstat_send(tsmsg, len); + } + SharedTabStat.tsa_used = 0; } @@ -850,14 +884,13 @@ pgstat_vacuum_tabstat(void) backend_read_statsfile(); /* - * Lookup our own database entry + * Lookup our own database entry; if not found, nothing to do. */ dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &MyDatabaseId, HASH_FIND, NULL); if (dbentry == NULL) return -1; - if (dbentry->tables == NULL) return 0; @@ -867,7 +900,7 @@ pgstat_vacuum_tabstat(void) msg.m_nentries = 0; /* - * Check for all tables if they still exist. + * Check for all tables listed in stats hashtable if they still exist. */ hash_seq_init(&hstat, dbentry->tables); while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL) @@ -892,7 +925,7 @@ pgstat_vacuum_tabstat(void) nobjects++; /* - * If the message is full, send it out and reinitialize ot zero + * If the message is full, send it out and reinitialize to zero */ if (msg.m_nentries >= PGSTAT_NUM_TABPURGE) { @@ -900,6 +933,7 @@ pgstat_vacuum_tabstat(void) +msg.m_nentries * sizeof(Oid); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE); + msg.m_databaseid = MyDatabaseId; pgstat_send(&msg, len); msg.m_nentries = 0; @@ -961,8 +995,8 @@ pgstat_vacuum_tabstat(void) if (dbid != InvalidOid) { - nobjects++; pgstat_drop_database(dbid); + nobjects++; } } @@ -1045,37 +1079,41 @@ pgstat_ping(void) } /* - * Create or enlarge the pgStatTabstatMessages array + * Enlarge a TabStatArray */ static void -more_tabstat_space(void) +more_tabstat_space(TabStatArray *tsarr) { PgStat_MsgTabstat *newMessages; PgStat_MsgTabstat **msgArray; - int newAlloc = pgStatTabstatAlloc + TABSTAT_QUANTUM; + int newAlloc; int i; + AssertArg(PointerIsValid(tsarr)); + + newAlloc = tsarr->tsa_alloc + TABSTAT_QUANTUM; + /* Create (another) quantum of message buffers */ newMessages = (PgStat_MsgTabstat *) MemoryContextAllocZero(TopMemoryContext, sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM); /* Create or enlarge the pointer array */ - if (pgStatTabstatMessages == NULL) + if (tsarr->tsa_messages == NULL) msgArray = (PgStat_MsgTabstat **) MemoryContextAlloc(TopMemoryContext, sizeof(PgStat_MsgTabstat *) * newAlloc); else msgArray = (PgStat_MsgTabstat **) - repalloc(pgStatTabstatMessages, + repalloc(tsarr->tsa_messages, sizeof(PgStat_MsgTabstat *) * newAlloc); for (i = 0; i < TABSTAT_QUANTUM; i++) - msgArray[pgStatTabstatAlloc + i] = newMessages++; - pgStatTabstatMessages = msgArray; - pgStatTabstatAlloc = newAlloc; + msgArray[tsarr->tsa_alloc + i] = newMessages++; + tsarr->tsa_messages = msgArray; + tsarr->tsa_alloc = newAlloc; - Assert(pgStatTabstatUsed < pgStatTabstatAlloc); + Assert(tsarr->tsa_used < tsarr->tsa_alloc); } /* ---------- @@ -1092,6 +1130,7 @@ pgstat_initstats(PgStat_Info *stats, Relation rel) { Oid rel_id = rel->rd_id; PgStat_TableEntry *useent; + TabStatArray *tsarr; PgStat_MsgTabstat *tsmsg; int mb; int i; @@ -1112,12 +1151,14 @@ pgstat_initstats(PgStat_Info *stats, Relation rel) return; } + tsarr = rel->rd_rel->relisshared ? &SharedTabStat : &RegularTabStat; + /* * Search the already-used message slots for this relation. */ - for (mb = 0; mb < pgStatTabstatUsed; mb++) + for (mb = 0; mb < tsarr->tsa_used; mb++) { - tsmsg = pgStatTabstatMessages[mb]; + tsmsg = tsarr->tsa_messages[mb]; for (i = tsmsg->m_nentries; --i >= 0;) { @@ -1146,14 +1187,14 @@ pgstat_initstats(PgStat_Info *stats, Relation rel) /* * If we ran out of message buffers, we just allocate more. */ - if (pgStatTabstatUsed >= pgStatTabstatAlloc) - more_tabstat_space(); + if (tsarr->tsa_used >= tsarr->tsa_alloc) + more_tabstat_space(tsarr); /* * Use the first entry of the next message buffer. */ - mb = pgStatTabstatUsed++; - tsmsg = pgStatTabstatMessages[mb]; + mb = tsarr->tsa_used++; + tsmsg = tsarr->tsa_messages[mb]; tsmsg->m_nentries = 1; useent = &tsmsg->m_entry[0]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); @@ -1183,13 +1224,13 @@ pgstat_count_xact_commit(void) * message buffer used without slots, causing the next report to tell * new xact-counters. */ - if (pgStatTabstatAlloc == 0) - more_tabstat_space(); + if (RegularTabStat.tsa_alloc == 0) + more_tabstat_space(&RegularTabStat); - if (pgStatTabstatUsed == 0) + if (RegularTabStat.tsa_used == 0) { - pgStatTabstatUsed++; - pgStatTabstatMessages[0]->m_nentries = 0; + RegularTabStat.tsa_used++; + RegularTabStat.tsa_messages[0]->m_nentries = 0; } } @@ -1215,13 +1256,13 @@ pgstat_count_xact_rollback(void) * message buffer used without slots, causing the next report to tell * new xact-counters. */ - if (pgStatTabstatAlloc == 0) - more_tabstat_space(); + if (RegularTabStat.tsa_alloc == 0) + more_tabstat_space(&RegularTabStat); - if (pgStatTabstatUsed == 0) + if (RegularTabStat.tsa_used == 0) { - pgStatTabstatUsed++; - pgStatTabstatMessages[0]->m_nentries = 0; + RegularTabStat.tsa_used++; + RegularTabStat.tsa_messages[0]->m_nentries = 0; } } @@ -1265,6 +1306,7 @@ pgstat_fetch_stat_dbentry(Oid dbid) PgStat_StatTabEntry * pgstat_fetch_stat_tabentry(Oid relid) { + Oid dbid; PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; @@ -1275,26 +1317,38 @@ pgstat_fetch_stat_tabentry(Oid relid) backend_read_statsfile(); /* - * Lookup our database. + * Lookup our database, then look in its table hash table. */ + dbid = MyDatabaseId; dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, - (void *) &MyDatabaseId, + (void *) &dbid, HASH_FIND, NULL); - if (dbentry == NULL) - return NULL; + if (dbentry != NULL && dbentry->tables != NULL) + { + tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables, + (void *) &relid, + HASH_FIND, NULL); + if (tabentry) + return tabentry; + } /* - * Now inside the DB's table hash table lookup the requested one. + * If we didn't find it, maybe it's a shared table. */ - if (dbentry->tables == NULL) - return NULL; - tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables, - (void *) &relid, - HASH_FIND, NULL); - if (tabentry == NULL) - return NULL; + dbid = InvalidOid; + dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, + (void *) &dbid, + HASH_FIND, NULL); + if (dbentry != NULL && dbentry->tables != NULL) + { + tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables, + (void *) &relid, + HASH_FIND, NULL); + if (tabentry) + return tabentry; + } - return tabentry; + return NULL; } @@ -2107,18 +2161,23 @@ pgstat_add_backend(PgStat_MsgHdr *msg) /* * Lookup the hash table entry for the specified database. If no hash - * table entry exists, initialize it. + * table entry exists, initialize it, if the create parameter is true. + * Else, return NULL. */ static PgStat_StatDBEntry * -pgstat_get_db_entry(Oid databaseid) +pgstat_get_db_entry(Oid databaseid, bool create) { PgStat_StatDBEntry *result; bool found; + HASHACTION action = (create ? HASH_ENTER : HASH_FIND); /* Lookup or create the hash table entry for this database */ result = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, &databaseid, - HASH_ENTER, &found); + action, &found); + + if (!create && !found) + return NULL; /* If not found, initialize the new one. */ if (!found) @@ -2387,7 +2446,7 @@ pgstat_write_statsfile(void) * pgstat_read_statsfile() - * * Reads in an existing statistics collector and initializes the - * databases hash table (who's entries point to the tables hash tables) + * databases' hash table (whose entries point to the tables' hash tables) * and the current backend table. * ---------- */ @@ -2507,10 +2566,15 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, dbentry->n_backends = 0; /* - * Don't collect tables if not the requested DB + * Don't collect tables if not the requested DB (or the + * shared-table info) */ - if (onlydb != InvalidOid && onlydb != dbbuf.databaseid) + if (onlydb != InvalidOid) + { + if (dbbuf.databaseid != onlydb && + dbbuf.databaseid != InvalidOid) break; + } memset(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = sizeof(Oid); @@ -2588,12 +2652,12 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, * backend table. */ if (use_mcxt == NULL) - *betab = (PgStat_StatBeEntry *) palloc( - sizeof(PgStat_StatBeEntry) * maxbackends); + *betab = (PgStat_StatBeEntry *) + palloc(sizeof(PgStat_StatBeEntry) * maxbackends); else - *betab = (PgStat_StatBeEntry *) MemoryContextAlloc( - use_mcxt, - sizeof(PgStat_StatBeEntry) * maxbackends); + *betab = (PgStat_StatBeEntry *) + MemoryContextAlloc(use_mcxt, + sizeof(PgStat_StatBeEntry) * maxbackends); break; /* @@ -2738,14 +2802,16 @@ pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len) PgStat_StatDBEntry *dbentry; /* - * Lookup the database in the hashtable. - * - * XXX this creates the entry if it doesn't exist. Is this a problem? (We - * could leak an entry if we send an autovac message and the database is - * later destroyed, _and_ the messages are rearranged. Doesn't seem very - * likely though.) Not sure what to do about it. + * 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); + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); + if (dbentry == NULL) + return; /* * Store the last autovacuum time in the database entry. @@ -2765,8 +2831,19 @@ pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len) PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; bool found; + bool create; + + /* + * If we don't know about the database, ignore the message, because it + * may be autovacuum processing a template database. But if the message + * is for database InvalidOid, don't ignore it, because we are getting + * a message from vacuuming a shared relation. + */ + create = (msg->m_databaseid == InvalidOid); - dbentry = pgstat_get_db_entry(msg->m_databaseid); + dbentry = pgstat_get_db_entry(msg->m_databaseid, create); + if (dbentry == NULL) + return; tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), HASH_ENTER, &found); @@ -2819,7 +2896,12 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) PgStat_StatTabEntry *tabentry; bool found; - dbentry = pgstat_get_db_entry(msg->m_databaseid); + /* + * Note that we do create the database entry here, as opposed to what + * we do on AutovacStart and Vacuum messages. This is because + * autovacuum never executes ANALYZE on template databases. + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid, true); tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), HASH_ENTER, &found); @@ -2902,7 +2984,7 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len) if (pgstat_add_backend(&msg->m_hdr) < 0) return; - dbentry = pgstat_get_db_entry(msg->m_databaseid); + dbentry = pgstat_get_db_entry(msg->m_databaseid, true); /* * If the database is marked for destroy, this is a delayed UDP packet @@ -2994,7 +3076,13 @@ pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len) if (pgstat_add_backend(&msg->m_hdr) < 0) return; - dbentry = pgstat_get_db_entry(msg->m_databaseid); + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); + + /* + * No need to purge if we don't even know the database. + */ + if (!dbentry || !dbentry->tables) + return; /* * If the database is marked for destroy, this is a delayed UDP packet @@ -3037,12 +3125,13 @@ pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len) /* * Lookup the database in the hashtable. */ - dbentry = pgstat_get_db_entry(msg->m_databaseid); + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); /* * Mark the database for destruction. */ - dbentry->destroy = PGSTAT_DESTROY_COUNT; + if (dbentry) + dbentry->destroy = PGSTAT_DESTROY_COUNT; } @@ -3065,9 +3154,12 @@ pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len) return; /* - * Lookup the database in the hashtable. + * Lookup the database in the hashtable. Nothing to do if not there. */ - dbentry = pgstat_get_db_entry(msg->m_databaseid); + dbentry = pgstat_get_db_entry(msg->m_databaseid, false); + + if (!dbentry) + return; /* * We simply throw away all the database's table entries by |