aboutsummaryrefslogtreecommitdiff
path: root/src/backend/postmaster/pgstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/postmaster/pgstat.c')
-rw-r--r--src/backend/postmaster/pgstat.c281
1 files changed, 266 insertions, 15 deletions
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 325bb8b451a..d6096fd2b8f 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.99 2005/07/04 04:51:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.100 2005/07/14 05:13:40 tgl Exp $
* ----------
*/
#include "postgres.h"
@@ -38,6 +38,7 @@
#include "libpq/pqsignal.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "postmaster/autovacuum.h"
#include "postmaster/fork_process.h"
#include "postmaster/postmaster.h"
#include "storage/backendid.h"
@@ -167,7 +168,7 @@ static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
int *numbackends);
static void backend_read_statsfile(void);
-static void pgstat_setheader(PgStat_MsgHdr *hdr, int mtype);
+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);
@@ -177,6 +178,9 @@ 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);
static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);
+static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len);
+static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len);
+static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len);
/* ------------------------------------------------------------
@@ -618,6 +622,27 @@ pgstat_beterm(int pid)
}
+/* ----------
+ * pgstat_report_autovac() -
+ *
+ * Called from autovacuum.c to report startup of an autovacuum process.
+ * ----------
+ */
+void
+pgstat_report_autovac(void)
+{
+ PgStat_MsgAutovacStart msg;
+
+ if (pgStatSock < 0)
+ return;
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START);
+ msg.m_databaseid = MyDatabaseId;
+ msg.m_start_time = GetCurrentTimestamp();
+
+ pgstat_send(&msg, sizeof(msg));
+}
+
/* ------------------------------------------------------------
* Public functions used by backends follow
*------------------------------------------------------------
@@ -652,6 +677,51 @@ pgstat_bestart(void)
on_proc_exit(pgstat_beshutdown_hook, 0);
}
+/* ---------
+ * pgstat_report_vacuum() -
+ *
+ * Tell the collector about the table we just vacuumed.
+ * ---------
+ */
+void
+pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples)
+{
+ PgStat_MsgVacuum msg;
+
+ if (pgStatSock < 0)
+ return;
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM);
+ msg.m_databaseid = MyDatabaseId;
+ msg.m_tableoid = tableoid;
+ msg.m_analyze = analyze;
+ 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, PgStat_Counter livetuples,
+ PgStat_Counter deadtuples)
+{
+ PgStat_MsgAnalyze msg;
+
+ if (pgStatSock < 0)
+ return;
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
+ msg.m_databaseid = MyDatabaseId;
+ msg.m_tableoid = tableoid;
+ 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
@@ -1279,7 +1349,7 @@ pgstat_fetch_stat_numbackends(void)
* ----------
*/
static void
-pgstat_setheader(PgStat_MsgHdr *hdr, int mtype)
+pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype)
{
hdr->m_type = mtype;
hdr->m_backendid = MyBackendId;
@@ -1653,6 +1723,18 @@ PgstatCollectorMain(int argc, char *argv[])
nread);
break;
+ case PGSTAT_MTYPE_AUTOVAC_START:
+ pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, nread);
+ break;
+
+ case PGSTAT_MTYPE_VACUUM:
+ pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, nread);
+ break;
+
+ case PGSTAT_MTYPE_ANALYZE:
+ pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, nread);
+ break;
+
default:
break;
}
@@ -2049,6 +2131,7 @@ pgstat_get_db_entry(Oid databaseid)
result->n_blocks_fetched = 0;
result->n_blocks_hit = 0;
result->destroy = 0;
+ result->last_autovac_time = 0;
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
@@ -2136,6 +2219,7 @@ pgstat_write_statsfile(void)
PgStat_StatBeDead *deadbe;
FILE *fpout;
int i;
+ int32 format_id;
/*
* Open the statistics temp file to write out the current values.
@@ -2151,6 +2235,12 @@ pgstat_write_statsfile(void)
}
/*
+ * Write the file header --- currently just a format ID.
+ */
+ format_id = PGSTAT_FILE_FORMAT_ID;
+ fwrite(&format_id, sizeof(format_id), 1, fpout);
+
+ /*
* Walk through the database table.
*/
hash_seq_init(&hstat, pgStatDBHash);
@@ -2182,7 +2272,7 @@ pgstat_write_statsfile(void)
}
/*
- * Write out the DB line including the number of life backends.
+ * Write out the DB line including the number of live backends.
*/
fputc('D', fpout);
fwrite(dbentry, sizeof(PgStat_StatDBEntry), 1, fpout);
@@ -2216,7 +2306,7 @@ pgstat_write_statsfile(void)
}
/*
- * At least we think this is still a life table. Print it's
+ * At least we think this is still a live table. Print its
* access stats.
*/
fputc('T', fpout);
@@ -2312,6 +2402,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
HASHCTL hash_ctl;
HTAB *tabhash = NULL;
FILE *fpin;
+ int32 format_id;
int maxbackends = 0;
int havebackends = 0;
bool found;
@@ -2319,12 +2410,13 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
int mcxt_flags;
/*
- * If running in the collector we use the DynaHashCxt memory context.
- * If running in a backend, we use the 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!
+ * If running in the collector or the autovacuum process, we use the
+ * DynaHashCxt memory context. If running in a backend, we use the
+ * 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!
*/
- if (pgStatRunningInCollector)
+ if (pgStatRunningInCollector || IsAutoVacuumProcess())
{
use_mcxt = NULL;
mcxt_flags = 0;
@@ -2364,6 +2456,17 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
return;
/*
+ * Verify it's of the expected format.
+ */
+ if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id)
+ || format_id != PGSTAT_FILE_FORMAT_ID)
+ {
+ ereport(pgStatRunningInCollector ? LOG : WARNING,
+ (errmsg("corrupted pgstat.stat file")));
+ goto done;
+ }
+
+ /*
* We found an existing collector stats file. Read it and put all the
* hashtable entries into place.
*/
@@ -2552,18 +2655,35 @@ done:
*
* Because we store the hash tables in TopTransactionContext, the result
* is good for the entire current main transaction.
+ *
+ * Inside the autovacuum process, the statfile is assumed to be valid
+ * "forever", that is one iteration, within one database. This means
+ * we only consider the statistics as they were when the autovacuum
+ * iteration started.
*/
static void
backend_read_statsfile(void)
{
- TransactionId topXid = GetTopTransactionId();
-
- if (!TransactionIdEquals(pgStatDBHashXact, topXid))
+ if (IsAutoVacuumProcess())
{
Assert(!pgStatRunningInCollector);
- pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
+ /* already read it? */
+ if (pgStatDBHash)
+ return;
+ pgstat_read_statsfile(&pgStatDBHash, InvalidOid,
&pgStatBeTable, &pgStatNumBackends);
- pgStatDBHashXact = topXid;
+ }
+ else
+ {
+ TransactionId topXid = GetTopTransactionId();
+
+ if (!TransactionIdEquals(pgStatDBHashXact, topXid))
+ {
+ Assert(!pgStatRunningInCollector);
+ pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
+ &pgStatBeTable, &pgStatNumBackends);
+ pgStatDBHashXact = topXid;
+ }
}
}
@@ -2606,6 +2726,129 @@ 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.
+ *
+ * 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.
+ */
+ dbentry = pgstat_get_db_entry(msg->m_databaseid);
+
+ /*
+ * 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;
+ bool found;
+
+ dbentry = pgstat_get_db_entry(msg->m_databaseid);
+
+ tabentry = hash_search(dbentry->tables, &(msg->m_tableoid),
+ HASH_ENTER, &found);
+
+ /*
+ * If we are creating the entry, initialize it.
+ */
+ if (!found)
+ {
+ tabentry->tableid = msg->m_tableoid;
+
+ tabentry->tuples_returned = 0;
+ tabentry->tuples_fetched = 0;
+ tabentry->tuples_inserted = msg->m_tuples;
+ tabentry->tuples_deleted = 0;
+ tabentry->tuples_updated = 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;
+ }
+ else
+ {
+ tabentry->n_dead_tuples = 0;
+ tabentry->n_live_tuples = msg->m_tuples;
+ if (msg->m_analyze)
+ tabentry->last_anl_tuples = msg->m_tuples;
+ }
+}
+
+/* ----------
+ * pgstat_recv_analyze() -
+ *
+ * Process an ANALYZE message.
+ * ----------
+ */
+static void
+pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
+{
+ PgStat_StatDBEntry *dbentry;
+ PgStat_StatTabEntry *tabentry;
+ bool found;
+
+ dbentry = pgstat_get_db_entry(msg->m_databaseid);
+
+ tabentry = hash_search(dbentry->tables, &(msg->m_tableoid),
+ HASH_ENTER, &found);
+
+ /*
+ * If we are creating the entry, initialize it.
+ */
+ if (!found)
+ {
+ tabentry->tableid = msg->m_tableoid;
+
+ tabentry->tuples_returned = 0;
+ tabentry->tuples_fetched = 0;
+ tabentry->tuples_inserted = 0;
+ tabentry->tuples_deleted = 0;
+ tabentry->tuples_updated = 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;
+ }
+ else
+ {
+ 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() -
@@ -2690,6 +2933,10 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
tabentry->tuples_deleted = tabmsg[i].t_tuples_deleted;
tabentry->blocks_fetched = tabmsg[i].t_blocks_fetched;
tabentry->blocks_hit = tabmsg[i].t_blocks_hit;
+
+ tabentry->n_live_tuples = tabmsg[i].t_tuples_inserted;
+ tabentry->n_dead_tuples = tabmsg[i].t_tuples_updated +
+ tabmsg[i].t_tuples_deleted;
tabentry->destroy = 0;
}
@@ -2706,6 +2953,10 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
tabentry->tuples_deleted += tabmsg[i].t_tuples_deleted;
tabentry->blocks_fetched += tabmsg[i].t_blocks_fetched;
tabentry->blocks_hit += tabmsg[i].t_blocks_hit;
+
+ tabentry->n_live_tuples += tabmsg[i].t_tuples_inserted;
+ tabentry->n_dead_tuples += tabmsg[i].t_tuples_updated +
+ tabmsg[i].t_tuples_deleted;
}
/*