Commit d5db3abf authored by Tom Lane's avatar Tom Lane

Modify pgstats code to reduce performance penalties from oversized stats data

files: avoid creating stats hashtable entries for tables that aren't being
touched except by vacuum/analyze, ensure that entries for dropped tables are
removed promptly, and tweak the data layout to avoid storing useless struct
padding.  Also improve the performance of pgstat_vacuum_tabstat(), and make
sure that autovacuum invokes it exactly once per autovac cycle rather than
multiple times or not at all.  This should cure recent complaints about 8.1
showing much higher stats I/O volume than was seen in 8.0.  It'd still be a
good idea to revisit the design with an eye to not re-writing the entire
stats dataset every half second ... but that would be too much to backpatch,
I fear.
parent e1af35af
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.320 2006/01/04 19:16:24 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.321 2006/01/18 20:35:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "postmaster/autovacuum.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/smgr.h" #include "storage/smgr.h"
...@@ -324,9 +325,10 @@ vacuum(VacuumStmt *vacstmt, List *relids) ...@@ -324,9 +325,10 @@ vacuum(VacuumStmt *vacstmt, List *relids)
errhint("Use VACUUM FULL, then VACUUM FREEZE."))); errhint("Use VACUUM FULL, then VACUUM FREEZE.")));
/* /*
* Send info about dead objects to the statistics collector * Send info about dead objects to the statistics collector, unless
* we are in autovacuum --- autovacuum.c does this for itself.
*/ */
if (vacstmt->vacuum) if (vacstmt->vacuum && !IsAutoVacuumProcess())
pgstat_vacuum_tabstat(); pgstat_vacuum_tabstat();
/* /*
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.9 2006/01/04 21:06:31 tgl Exp $ * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.10 2006/01/18 20:35:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -508,6 +508,11 @@ process_whole_db(void) ...@@ -508,6 +508,11 @@ process_whole_db(void)
/* functions in indexes may want a snapshot set */ /* functions in indexes may want a snapshot set */
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
/*
* Clean up any dead statistics collector entries for this DB.
*/
pgstat_vacuum_tabstat();
dbRel = heap_open(DatabaseRelationId, AccessShareLock); dbRel = heap_open(DatabaseRelationId, AccessShareLock);
/* Must use a table scan, since there's no syscache for pg_database */ /* Must use a table scan, since there's no syscache for pg_database */
...@@ -572,6 +577,13 @@ do_autovacuum(PgStat_StatDBEntry *dbentry) ...@@ -572,6 +577,13 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
/* functions in indexes may want a snapshot set */ /* functions in indexes may want a snapshot set */
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
/*
* Clean up any dead statistics collector entries for this DB.
* We always want to do this exactly once per DB-processing cycle,
* even if we find nothing worth vacuuming in the database.
*/
pgstat_vacuum_tabstat();
/* /*
* StartTransactionCommand and CommitTransactionCommand will automatically * StartTransactionCommand and CommitTransactionCommand will automatically
* switch to other contexts. We need this one to keep the list of * switch to other contexts. We need this one to keep the list of
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* Copyright (c) 2001-2005, PostgreSQL Global Development Group * Copyright (c) 2001-2005, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.119 2006/01/04 21:06:31 tgl Exp $ * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.120 2006/01/18 20:35:05 tgl Exp $
* ---------- * ----------
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -728,7 +728,8 @@ pgstat_report_vacuum(Oid tableoid, bool shared, ...@@ -728,7 +728,8 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
{ {
PgStat_MsgVacuum msg; PgStat_MsgVacuum msg;
if (pgStatSock < 0) if (pgStatSock < 0 ||
!pgstat_collect_tuplelevel)
return; return;
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM);
...@@ -751,7 +752,8 @@ pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, ...@@ -751,7 +752,8 @@ pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples,
{ {
PgStat_MsgAnalyze msg; PgStat_MsgAnalyze msg;
if (pgStatSock < 0) if (pgStatSock < 0 ||
!pgstat_collect_tuplelevel)
return; return;
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
...@@ -880,26 +882,21 @@ pgstat_report_tabstat(void) ...@@ -880,26 +882,21 @@ pgstat_report_tabstat(void)
* Will tell the collector about objects he can get rid of. * Will tell the collector about objects he can get rid of.
* ---------- * ----------
*/ */
int void
pgstat_vacuum_tabstat(void) pgstat_vacuum_tabstat(void)
{ {
Relation dbrel; List *oidlist;
HeapScanDesc dbscan; Relation rel;
HeapTuple dbtup; HeapScanDesc scan;
Oid *dbidlist; HeapTuple tup;
int dbidalloc; PgStat_MsgTabpurge msg;
int dbidused;
HASH_SEQ_STATUS hstat; HASH_SEQ_STATUS hstat;
PgStat_StatDBEntry *dbentry; PgStat_StatDBEntry *dbentry;
PgStat_StatTabEntry *tabentry; PgStat_StatTabEntry *tabentry;
HeapTuple reltup;
int nobjects = 0;
PgStat_MsgTabpurge msg;
int len; int len;
int i;
if (pgStatSock < 0) if (pgStatSock < 0)
return 0; return;
/* /*
* If not done for this transaction, read the statistics collector stats * If not done for this transaction, read the statistics collector stats
...@@ -908,15 +905,55 @@ pgstat_vacuum_tabstat(void) ...@@ -908,15 +905,55 @@ pgstat_vacuum_tabstat(void)
backend_read_statsfile(); backend_read_statsfile();
/* /*
* Lookup our own database entry; if not found, nothing to do. * Read pg_database and make a list of OIDs of all existing databases
*/
oidlist = NIL;
rel = heap_open(DatabaseRelationId, AccessShareLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup));
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
/*
* Search the database hash table for dead databases and tell the
* collector to drop them.
*/
hash_seq_init(&hstat, pgStatDBHash);
while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL)
{
Oid dbid = dbentry->databaseid;
if (!list_member_oid(oidlist, dbid))
pgstat_drop_database(dbid);
}
/* Clean up */
list_free(oidlist);
/*
* Lookup our own database entry; if not found, nothing more to do.
*/ */
dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
(void *) &MyDatabaseId, (void *) &MyDatabaseId,
HASH_FIND, NULL); HASH_FIND, NULL);
if (dbentry == NULL) if (dbentry == NULL || dbentry->tables == NULL)
return -1; return;
if (dbentry->tables == NULL)
return 0; /*
* Similarly to above, make a list of all known relations in this DB.
*/
oidlist = NIL;
rel = heap_open(RelationRelationId, AccessShareLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup));
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
/* /*
* Initialize our messages table counter to zero * Initialize our messages table counter to zero
...@@ -929,27 +966,16 @@ pgstat_vacuum_tabstat(void) ...@@ -929,27 +966,16 @@ pgstat_vacuum_tabstat(void)
hash_seq_init(&hstat, dbentry->tables); hash_seq_init(&hstat, dbentry->tables);
while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL) while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL)
{ {
/* if (list_member_oid(oidlist, tabentry->tableid))
* Check if this relation is still alive by looking up it's pg_class
* tuple in the system catalog cache.
*/
reltup = SearchSysCache(RELOID,
ObjectIdGetDatum(tabentry->tableid),
0, 0, 0);
if (HeapTupleIsValid(reltup))
{
ReleaseSysCache(reltup);
continue; continue;
}
/* /*
* Add this table's Oid to the message * Not there, so add this table's Oid to the message
*/ */
msg.m_tableid[msg.m_nentries++] = tabentry->tableid; msg.m_tableid[msg.m_nentries++] = tabentry->tableid;
nobjects++;
/* /*
* If the message is full, send it out and reinitialize to zero * If the message is full, send it out and reinitialize to empty
*/ */
if (msg.m_nentries >= PGSTAT_NUM_TABPURGE) if (msg.m_nentries >= PGSTAT_NUM_TABPURGE)
{ {
...@@ -977,62 +1003,8 @@ pgstat_vacuum_tabstat(void) ...@@ -977,62 +1003,8 @@ pgstat_vacuum_tabstat(void)
pgstat_send(&msg, len); pgstat_send(&msg, len);
} }
/* /* Clean up */
* Read pg_database and remember the Oid's of all existing databases list_free(oidlist);
*/
dbidalloc = 256;
dbidused = 0;
dbidlist = (Oid *) palloc(sizeof(Oid) * dbidalloc);
dbrel = heap_open(DatabaseRelationId, AccessShareLock);
dbscan = heap_beginscan(dbrel, SnapshotNow, 0, NULL);
while ((dbtup = heap_getnext(dbscan, ForwardScanDirection)) != NULL)
{
if (dbidused >= dbidalloc)
{
dbidalloc *= 2;
dbidlist = (Oid *) repalloc((char *) dbidlist,
sizeof(Oid) * dbidalloc);
}
dbidlist[dbidused++] = HeapTupleGetOid(dbtup);
}
heap_endscan(dbscan);
heap_close(dbrel, AccessShareLock);
/*
* Search the database hash table for dead databases and tell the
* collector to drop them as well.
*/
hash_seq_init(&hstat, pgStatDBHash);
while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL)
{
Oid dbid = dbentry->databaseid;
for (i = 0; i < dbidused; i++)
{
if (dbidlist[i] == dbid)
{
dbid = InvalidOid;
break;
}
}
if (dbid != InvalidOid)
{
pgstat_drop_database(dbid);
nobjects++;
}
}
/*
* Free the dbid list.
*/
pfree(dbidlist);
/*
* Tell the caller how many removeable objects we found
*/
return nobjects;
} }
...@@ -1040,9 +1012,8 @@ pgstat_vacuum_tabstat(void) ...@@ -1040,9 +1012,8 @@ pgstat_vacuum_tabstat(void)
* pgstat_drop_database() - * pgstat_drop_database() -
* *
* Tell the collector that we just dropped a database. * Tell the collector that we just dropped a database.
* This is the only message that shouldn't get lost in space. Otherwise * (If the message gets lost, we will still clean the dead DB eventually
* the collector will keep the statistics for the dead DB until his * via future invocations of pgstat_vacuum_tabstat().)
* stats file got removed while the postmaster is down.
* ---------- * ----------
*/ */
static void static void
...@@ -1059,6 +1030,34 @@ pgstat_drop_database(Oid databaseid) ...@@ -1059,6 +1030,34 @@ pgstat_drop_database(Oid databaseid)
} }
/* ----------
* pgstat_drop_relation() -
*
* Tell the collector that we just dropped a relation.
* (If the message gets lost, we will still clean the dead entry eventually
* via future invocations of pgstat_vacuum_tabstat().)
* ----------
*/
void
pgstat_drop_relation(Oid relid)
{
PgStat_MsgTabpurge msg;
int len;
if (pgStatSock < 0)
return;
msg.m_tableid[0] = relid;
msg.m_nentries = 1;
len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) + sizeof(Oid);
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
msg.m_databaseid = MyDatabaseId;
pgstat_send(&msg, len);
}
/* ---------- /* ----------
* pgstat_reset_counters() - * pgstat_reset_counters() -
* *
...@@ -2327,13 +2326,15 @@ pgstat_write_statsfile(void) ...@@ -2327,13 +2326,15 @@ pgstat_write_statsfile(void)
} }
/* /*
* Write out the DB line including the number of live backends. * Write out the DB entry including the number of live backends.
* We don't write the tables pointer since it's of no use to any
* other process.
*/ */
fputc('D', fpout); fputc('D', fpout);
fwrite(dbentry, sizeof(PgStat_StatDBEntry), 1, fpout); fwrite(dbentry, offsetof(PgStat_StatDBEntry, tables), 1, fpout);
/* /*
* Walk through the databases access stats per table. * Walk through the database's access stats per table.
*/ */
hash_seq_init(&tstat, dbentry->tables); hash_seq_init(&tstat, dbentry->tables);
while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&tstat)) != NULL) while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&tstat)) != NULL)
...@@ -2349,19 +2350,17 @@ pgstat_write_statsfile(void) ...@@ -2349,19 +2350,17 @@ pgstat_write_statsfile(void)
if (hash_search(dbentry->tables, if (hash_search(dbentry->tables,
(void *) &(tabentry->tableid), (void *) &(tabentry->tableid),
HASH_REMOVE, NULL) == NULL) HASH_REMOVE, NULL) == NULL)
{
ereport(ERROR, ereport(ERROR,
(errmsg("tables hash table for " (errmsg("tables hash table for "
"database %u corrupted during " "database %u corrupted during "
"cleanup --- abort", "cleanup --- abort",
dbentry->databaseid))); dbentry->databaseid)));
}
} }
continue; continue;
} }
/* /*
* At least we think this is still a live table. Print its access * At least we think this is still a live table. Emit its access
* stats. * stats.
*/ */
fputc('T', fpout); fputc('T', fpout);
...@@ -2383,34 +2382,51 @@ pgstat_write_statsfile(void) ...@@ -2383,34 +2382,51 @@ pgstat_write_statsfile(void)
for (i = 0; i < MaxBackends; i++) for (i = 0; i < MaxBackends; i++)
{ {
if (pgStatBeTable[i].procpid > 0) PgStat_StatBeEntry *beentry = &pgStatBeTable[i];
if (beentry->procpid > 0)
{ {
int len;
len = offsetof(PgStat_StatBeEntry, activity) +
strlen(beentry->activity) + 1;
fputc('B', fpout); fputc('B', fpout);
fwrite(&pgStatBeTable[i], sizeof(PgStat_StatBeEntry), 1, 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 * No more output to be done. Close the temp file and replace the old
* pgstat.stat with it. * pgstat.stat with it. The ferror() check replaces testing for error
* after each individual fputc or fwrite above.
*/ */
fputc('E', fpout); fputc('E', fpout);
if (fclose(fpout) < 0)
if (ferror(fpout))
{
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not write temporary statistics file \"%s\": %m",
PGSTAT_STAT_TMPFILE)));
fclose(fpout);
unlink(PGSTAT_STAT_TMPFILE);
}
else if (fclose(fpout) < 0)
{ {
ereport(LOG, ereport(LOG,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not close temporary statistics file \"%s\": %m", errmsg("could not close temporary statistics file \"%s\": %m",
PGSTAT_STAT_TMPFILE))); PGSTAT_STAT_TMPFILE)));
unlink(PGSTAT_STAT_TMPFILE);
} }
else else if (rename(PGSTAT_STAT_TMPFILE, PGSTAT_STAT_FILENAME) < 0)
{ {
if (rename(PGSTAT_STAT_TMPFILE, PGSTAT_STAT_FILENAME) < 0) ereport(LOG,
{ (errcode_for_file_access(),
ereport(LOG, errmsg("could not rename temporary statistics file \"%s\" to \"%s\": %m",
(errcode_for_file_access(), PGSTAT_STAT_TMPFILE, PGSTAT_STAT_FILENAME)));
errmsg("could not rename temporary statistics file \"%s\" to \"%s\": %m", unlink(PGSTAT_STAT_TMPFILE);
PGSTAT_STAT_TMPFILE, PGSTAT_STAT_FILENAME)));
}
} }
/* /*
...@@ -2427,11 +2443,9 @@ pgstat_write_statsfile(void) ...@@ -2427,11 +2443,9 @@ pgstat_write_statsfile(void)
if (hash_search(pgStatBeDead, if (hash_search(pgStatBeDead,
(void *) &(deadbe->procpid), (void *) &(deadbe->procpid),
HASH_REMOVE, NULL) == NULL) HASH_REMOVE, NULL) == NULL)
{
ereport(ERROR, ereport(ERROR,
(errmsg("dead-server-process hash table corrupted " (errmsg("dead-server-process hash table corrupted "
"during cleanup --- abort"))); "during cleanup --- abort")));
}
} }
} }
} }
...@@ -2468,6 +2482,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, ...@@ -2468,6 +2482,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
HTAB *tabhash = NULL; HTAB *tabhash = NULL;
FILE *fpin; FILE *fpin;
int32 format_id; int32 format_id;
int len;
int maxbackends = 0; int maxbackends = 0;
int havebackends = 0; int havebackends = 0;
bool found; bool found;
...@@ -2556,7 +2571,8 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, ...@@ -2556,7 +2571,8 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
* until a 'd' is encountered. * until a 'd' is encountered.
*/ */
case 'D': case 'D':
if (fread(&dbbuf, 1, sizeof(dbbuf), fpin) != sizeof(dbbuf)) if (fread(&dbbuf, 1, offsetof(PgStat_StatDBEntry, tables),
fpin) != offsetof(PgStat_StatDBEntry, tables))
{ {
ereport(pgStatRunningInCollector ? LOG : WARNING, ereport(pgStatRunningInCollector ? LOG : WARNING,
(errmsg("corrupted pgstat.stat file"))); (errmsg("corrupted pgstat.stat file")));
...@@ -2604,7 +2620,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, ...@@ -2604,7 +2620,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
HASH_ELEM | HASH_FUNCTION | mcxt_flags); HASH_ELEM | HASH_FUNCTION | mcxt_flags);
/* /*
* Arrange that following 'T's add entries to this databases * Arrange that following 'T's add entries to this database's
* tables hash table. * tables hash table.
*/ */
tabhash = dbentry->tables; tabhash = dbentry->tables;
...@@ -2621,7 +2637,8 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, ...@@ -2621,7 +2637,8 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
* 'T' A PgStat_StatTabEntry follows. * 'T' A PgStat_StatTabEntry follows.
*/ */
case 'T': case 'T':
if (fread(&tabbuf, 1, sizeof(tabbuf), fpin) != sizeof(tabbuf)) if (fread(&tabbuf, 1, sizeof(PgStat_StatTabEntry),
fpin) != sizeof(PgStat_StatTabEntry))
{ {
ereport(pgStatRunningInCollector ? LOG : WARNING, ereport(pgStatRunningInCollector ? LOG : WARNING,
(errmsg("corrupted pgstat.stat file"))); (errmsg("corrupted pgstat.stat file")));
...@@ -2687,13 +2704,27 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, ...@@ -2687,13 +2704,27 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
if (havebackends >= maxbackends) if (havebackends >= maxbackends)
goto done; 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. * Read it directly into the table.
*/ */
beentry = &(*betab)[havebackends]; beentry = &(*betab)[havebackends];
if (fread(beentry, 1, sizeof(PgStat_StatBeEntry), fpin) != if (fread(beentry, 1, len, fpin) != len)
sizeof(PgStat_StatBeEntry))
{ {
ereport(pgStatRunningInCollector ? LOG : WARNING, ereport(pgStatRunningInCollector ? LOG : WARNING,
(errmsg("corrupted pgstat.stat file"))); (errmsg("corrupted pgstat.stat file")));
...@@ -2872,57 +2903,25 @@ pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len) ...@@ -2872,57 +2903,25 @@ pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len)
{ {
PgStat_StatDBEntry *dbentry; PgStat_StatDBEntry *dbentry;
PgStat_StatTabEntry *tabentry; PgStat_StatTabEntry *tabentry;
bool found;
bool create;
/* /*
* If we don't know about the database, ignore the message, because it may * Don't create either the database or table entry if it doesn't already
* be autovacuum processing a template database. But if the message is * exist. This avoids bloating the stats with entries for stuff that is
* for database InvalidOid, don't ignore it, because we are getting a * only touched by vacuum and not by live operations.
* message from vacuuming a shared relation.
*/ */
create = (msg->m_databaseid == InvalidOid); dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
dbentry = pgstat_get_db_entry(msg->m_databaseid, create);
if (dbentry == NULL) if (dbentry == NULL)
return; return;
tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), tabentry = hash_search(dbentry->tables, &(msg->m_tableoid),
HASH_ENTER, &found); HASH_FIND, NULL);
if (tabentry == NULL)
/* return;
* If we are creating the entry, initialize it.
*/
if (!found)
{
tabentry->numscans = 0;
tabentry->tuples_returned = 0;
tabentry->tuples_fetched = 0;
tabentry->tuples_inserted = 0;
tabentry->tuples_updated = 0;
tabentry->tuples_deleted = 0;
tabentry->n_live_tuples = msg->m_tuples;
tabentry->n_dead_tuples = 0;
if (msg->m_analyze)
tabentry->last_anl_tuples = msg->m_tuples;
else
tabentry->last_anl_tuples = 0;
tabentry->blocks_fetched = 0;
tabentry->blocks_hit = 0;
tabentry->destroy = 0; tabentry->n_live_tuples = msg->m_tuples;
} tabentry->n_dead_tuples = 0;
else if (msg->m_analyze)
{ tabentry->last_anl_tuples = msg->m_tuples;
tabentry->n_live_tuples = msg->m_tuples;
tabentry->n_dead_tuples = 0;
if (msg->m_analyze)
tabentry->last_anl_tuples = msg->m_tuples;
}
} }
/* ---------- /* ----------
...@@ -2936,46 +2935,24 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) ...@@ -2936,46 +2935,24 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
{ {
PgStat_StatDBEntry *dbentry; PgStat_StatDBEntry *dbentry;
PgStat_StatTabEntry *tabentry; PgStat_StatTabEntry *tabentry;
bool found;
/* /*
* Note that we do create the database entry here, as opposed to what we * Don't create either the database or table entry if it doesn't already
* do on AutovacStart and Vacuum messages. This is because autovacuum * exist. This avoids bloating the stats with entries for stuff that is
* never executes ANALYZE on template databases. * only touched by analyze and not by live operations.
*/ */
dbentry = pgstat_get_db_entry(msg->m_databaseid, true); dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
if (dbentry == NULL)
return;
tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), tabentry = hash_search(dbentry->tables, &(msg->m_tableoid),
HASH_ENTER, &found); HASH_FIND, NULL);
if (tabentry == NULL)
/* return;
* If we are creating the entry, initialize it.
*/
if (!found)
{
tabentry->numscans = 0;
tabentry->tuples_returned = 0;
tabentry->tuples_fetched = 0;
tabentry->tuples_inserted = 0;
tabentry->tuples_updated = 0;
tabentry->tuples_deleted = 0;
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;
tabentry->blocks_fetched = 0;
tabentry->blocks_hit = 0;
tabentry->destroy = 0; tabentry->n_live_tuples = msg->m_live_tuples;
} tabentry->n_dead_tuples = msg->m_dead_tuples;
else tabentry->last_anl_tuples = msg->m_live_tuples + msg->m_dead_tuples;
{
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;
}
} }
/* ---------- /* ----------
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.94 2005/11/22 18:17:21 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.95 2006/01/18 20:35:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "access/xact.h" #include "access/xact.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "pgstat.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
...@@ -469,6 +470,9 @@ smgr_internal_unlink(RelFileNode rnode, int which, bool isTemp, bool isRedo) ...@@ -469,6 +470,9 @@ smgr_internal_unlink(RelFileNode rnode, int which, bool isTemp, bool isRedo)
*/ */
FreeSpaceMapForgetRel(&rnode); FreeSpaceMapForgetRel(&rnode);
/* Tell the stats collector to forget it immediately, too. */
pgstat_drop_relation(rnode.relNode);
/* /*
* And delete the physical files. * And delete the physical files.
* *
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* Copyright (c) 2001-2005, PostgreSQL Global Development Group * Copyright (c) 2001-2005, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/include/pgstat.h,v 1.40 2005/12/31 19:39:11 momjian Exp $ * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.41 2006/01/18 20:35:06 tgl Exp $
* ---------- * ----------
*/ */
#ifndef PGSTAT_H #ifndef PGSTAT_H
...@@ -271,7 +271,7 @@ typedef union PgStat_Msg ...@@ -271,7 +271,7 @@ typedef union PgStat_Msg
* ------------------------------------------------------------ * ------------------------------------------------------------
*/ */
#define PGSTAT_FILE_FORMAT_ID 0x01A5BC93 #define PGSTAT_FILE_FORMAT_ID 0x01A5BC94
/* ---------- /* ----------
* PgStat_StatDBEntry The collector's data per database * PgStat_StatDBEntry The collector's data per database
...@@ -280,14 +280,19 @@ typedef union PgStat_Msg ...@@ -280,14 +280,19 @@ typedef union PgStat_Msg
typedef struct PgStat_StatDBEntry typedef struct PgStat_StatDBEntry
{ {
Oid databaseid; Oid databaseid;
HTAB *tables; int destroy;
int n_backends; int n_backends;
PgStat_Counter n_xact_commit; PgStat_Counter n_xact_commit;
PgStat_Counter n_xact_rollback; PgStat_Counter n_xact_rollback;
PgStat_Counter n_blocks_fetched; PgStat_Counter n_blocks_fetched;
PgStat_Counter n_blocks_hit; PgStat_Counter n_blocks_hit;
int destroy;
TimestampTz last_autovac_time; TimestampTz last_autovac_time;
/*
* tables must be last in the struct, because we don't write the pointer
* out to the stats file.
*/
HTAB *tables;
} PgStat_StatDBEntry; } PgStat_StatDBEntry;
...@@ -301,10 +306,9 @@ typedef struct PgStat_StatBeEntry ...@@ -301,10 +306,9 @@ typedef struct PgStat_StatBeEntry
int procpid; int procpid;
TimestampTz start_timestamp; TimestampTz start_timestamp;
TimestampTz activity_start_timestamp; TimestampTz activity_start_timestamp;
char activity[PGSTAT_ACTIVITY_SIZE];
/* /*
* The following fields are initialized by the BESTART message. If we have * These fields are initialized by the BESTART message. If we have
* received messages from a backend before we have received its BESTART, * received messages from a backend before we have received its BESTART,
* these fields will be uninitialized: userid and databaseid will be * these fields will be uninitialized: userid and databaseid will be
* InvalidOid, and clientaddr will be undefined. * InvalidOid, and clientaddr will be undefined.
...@@ -312,6 +316,12 @@ typedef struct PgStat_StatBeEntry ...@@ -312,6 +316,12 @@ typedef struct PgStat_StatBeEntry
Oid userid; Oid userid;
Oid databaseid; Oid databaseid;
SockAddr clientaddr; 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_StatBeEntry;
...@@ -321,6 +331,8 @@ typedef struct PgStat_StatBeEntry ...@@ -321,6 +331,8 @@ typedef struct PgStat_StatBeEntry
* about backends that are known to be * about backends that are known to be
* dead for some seconds. This info is held * dead for some seconds. This info is held
* in a hash table of these structs. * in a hash table of these structs.
*
* (This struct is not used in the stats file.)
* ---------- * ----------
*/ */
typedef struct PgStat_StatBeDead typedef struct PgStat_StatBeDead
...@@ -338,6 +350,7 @@ typedef struct PgStat_StatBeDead ...@@ -338,6 +350,7 @@ typedef struct PgStat_StatBeDead
typedef struct PgStat_StatTabEntry typedef struct PgStat_StatTabEntry
{ {
Oid tableid; Oid tableid;
int destroy;
PgStat_Counter numscans; PgStat_Counter numscans;
...@@ -354,8 +367,6 @@ typedef struct PgStat_StatTabEntry ...@@ -354,8 +367,6 @@ typedef struct PgStat_StatTabEntry
PgStat_Counter blocks_fetched; PgStat_Counter blocks_fetched;
PgStat_Counter blocks_hit; PgStat_Counter blocks_hit;
int destroy;
} PgStat_StatTabEntry; } PgStat_StatTabEntry;
...@@ -400,7 +411,8 @@ extern void pgstat_report_vacuum(Oid tableoid, bool shared, ...@@ -400,7 +411,8 @@ extern void pgstat_report_vacuum(Oid tableoid, bool shared,
extern void pgstat_report_analyze(Oid tableoid, bool shared, extern void pgstat_report_analyze(Oid tableoid, bool shared,
PgStat_Counter livetuples, PgStat_Counter livetuples,
PgStat_Counter deadtuples); PgStat_Counter deadtuples);
extern int pgstat_vacuum_tabstat(void); extern void pgstat_vacuum_tabstat(void);
extern void pgstat_drop_relation(Oid relid);
extern void pgstat_reset_counters(void); extern void pgstat_reset_counters(void);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment