-<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.45 2007/02/01 00:28:17 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.46 2007/02/07 23:11:29 tgl Exp $ -->
<chapter id="monitoring">
<title>Monitoring Database Activity</title>
queries on the statistics and correlate the results without worrying that
the numbers are changing underneath you. But if you want to see new
results with each query, be sure to do the queries outside any transaction
- block.
+ block. Alternatively, you can invoke
+ <function>pg_stat_clear_snapshot</function>(), which will discard the
+ current transaction's statistics snapshot (if any). The next use of
+ statistical information will cause a new snapshot to be fetched.
</para>
<table id="monitoring-stats-views-table">
</entry>
</row>
+ <row>
+ <entry><literal><function>pg_stat_clear_snapshot</function>()</literal></entry>
+ <entry><type>void</type></entry>
+ <entry>
+ Discard the current statistics snapshot
+ </entry>
+ </row>
+
<row>
<entry><literal><function>pg_stat_reset</function>()</literal></entry>
- <entry><type>boolean</type></entry>
+ <entry><type>void</type></entry>
<entry>
- Reset all block-level and row-level statistics to zero
+ Reset all statistics counters for the current database to zero
+ (requires superuser privileges)
</entry>
</row>
</tbody>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.232 2007/02/01 19:10:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.233 2007/02/07 23:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
AtEOXact_Namespace(true);
/* smgrcommit already done */
AtEOXact_Files();
+ pgstat_clear_snapshot();
pgstat_count_xact_commit();
pgstat_report_txn_timestamp(0);
AtEOXact_Namespace(true);
/* smgrcommit already done */
AtEOXact_Files();
+ pgstat_clear_snapshot();
CurrentResourceOwner = NULL;
ResourceOwnerDelete(TopTransactionResourceOwner);
AtEOXact_Namespace(false);
smgrabort();
AtEOXact_Files();
+ pgstat_clear_snapshot();
pgstat_count_xact_rollback();
pgstat_report_txn_timestamp(0);
*
* Copyright (c) 2001-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.144 2007/01/26 20:06:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.145 2007/02/07 23:11:29 tgl Exp $
* ----------
*/
#include "postgres.h"
static int pgStatXactCommit = 0;
static int pgStatXactRollback = 0;
-static TransactionId pgStatDBHashXact = InvalidTransactionId;
+static MemoryContext pgStatLocalContext = NULL;
static HTAB *pgStatDBHash = NULL;
-static TransactionId pgStatLocalStatusXact = InvalidTransactionId;
static PgBackendStatus *localBackendStatusTable = NULL;
static int localNumBackends = 0;
static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
static void pgstat_drop_database(Oid databaseid);
static void pgstat_write_statsfile(void);
-static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb);
+static HTAB *pgstat_read_statsfile(Oid onlydb);
static void backend_read_statsfile(void);
static void pgstat_read_current_status(void);
static HTAB *pgstat_collect_oids(Oid catalogid);
+static void pgstat_setup_memcxt(void);
+
static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
static void pgstat_send(void *msg, int len);
static void
pgstat_read_current_status(void)
{
- TransactionId topXid = GetTopTransactionId();
volatile PgBackendStatus *beentry;
+ PgBackendStatus *localtable;
PgBackendStatus *localentry;
int i;
Assert(!pgStatRunningInCollector);
- if (TransactionIdEquals(pgStatLocalStatusXact, topXid))
+ if (localBackendStatusTable)
return; /* already done */
- localBackendStatusTable = (PgBackendStatus *)
- MemoryContextAlloc(TopTransactionContext,
+ pgstat_setup_memcxt();
+
+ localtable = (PgBackendStatus *)
+ MemoryContextAlloc(pgStatLocalContext,
sizeof(PgBackendStatus) * MaxBackends);
localNumBackends = 0;
beentry = BackendStatusArray;
- localentry = localBackendStatusTable;
+ localentry = localtable;
for (i = 1; i <= MaxBackends; i++)
{
/*
}
}
- pgStatLocalStatusXact = topXid;
+ /* Set the pointer only after completion of a valid table */
+ localBackendStatusTable = localtable;
}
* zero.
*/
pgStatRunningInCollector = true;
- pgstat_read_statsfile(&pgStatDBHash, InvalidOid);
+ pgStatDBHash = pgstat_read_statsfile(InvalidOid);
/*
* Setup the descriptor set for select(2). Since only one bit in the set
* databases' hash table (whose entries point to the tables' hash tables).
* ----------
*/
-static void
-pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
+static HTAB *
+pgstat_read_statsfile(Oid onlydb)
{
PgStat_StatDBEntry *dbentry;
PgStat_StatDBEntry dbbuf;
PgStat_StatTabEntry *tabentry;
PgStat_StatTabEntry tabbuf;
HASHCTL hash_ctl;
+ HTAB *dbhash;
HTAB *tabhash = NULL;
FILE *fpin;
int32 format_id;
bool found;
- MemoryContext use_mcxt;
- int mcxt_flags;
/*
- * 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!
+ * The tables will live in pgStatLocalContext.
*/
- if (pgStatRunningInCollector || IsAutoVacuumProcess())
- {
- use_mcxt = NULL;
- mcxt_flags = 0;
- }
- else
- {
- use_mcxt = TopTransactionContext;
- mcxt_flags = HASH_CONTEXT;
- }
+ pgstat_setup_memcxt();
/*
* Create the DB hashtable
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(PgStat_StatDBEntry);
hash_ctl.hash = oid_hash;
- hash_ctl.hcxt = use_mcxt;
- *dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
- HASH_ELEM | HASH_FUNCTION | mcxt_flags);
+ hash_ctl.hcxt = pgStatLocalContext;
+ dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
+ HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
/*
* Try to open the status file. If it doesn't exist, the backends simply
* with empty counters.
*/
if ((fpin = AllocateFile(PGSTAT_STAT_FILENAME, PG_BINARY_R)) == NULL)
- return;
+ return dbhash;
/*
* Verify it's of the expected format.
/*
* Add to the DB hash
*/
- dbentry = (PgStat_StatDBEntry *) hash_search(*dbhash,
+ dbentry = (PgStat_StatDBEntry *) hash_search(dbhash,
(void *) &dbbuf.databaseid,
HASH_ENTER,
&found);
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
hash_ctl.hash = oid_hash;
- hash_ctl.hcxt = use_mcxt;
+ hash_ctl.hcxt = pgStatLocalContext;
dbentry->tables = hash_create("Per-database table",
PGSTAT_TAB_HASH_SIZE,
&hash_ctl,
- HASH_ELEM | HASH_FUNCTION | mcxt_flags);
+ HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
/*
* Arrange that following 'T's add entries to this database's
done:
FreeFile(fpin);
+
+ return dbhash;
}
/*
- * If not done for this transaction, read the statistics collector
- * stats file into some hash tables.
- *
- * 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
- * "forever", that is one iteration, within one database. This means
- * we only consider the statistics as they were when the autovacuum
- * iteration started.
+ * If not already done, read the statistics collector stats file into
+ * some hash tables. The results will be kept until pgstat_clear_snapshot()
+ * is called (typically, at end of transaction).
*/
static void
backend_read_statsfile(void)
{
+ /* already read it? */
+ if (pgStatDBHash)
+ return;
+ Assert(!pgStatRunningInCollector);
+
+ /* Autovacuum wants stats about all databases */
if (IsAutoVacuumProcess())
- {
- /* already read it? */
- if (pgStatDBHash)
- return;
- Assert(!pgStatRunningInCollector);
- pgstat_read_statsfile(&pgStatDBHash, InvalidOid);
- }
+ pgStatDBHash = pgstat_read_statsfile(InvalidOid);
else
- {
- TransactionId topXid = GetTopTransactionId();
+ pgStatDBHash = pgstat_read_statsfile(MyDatabaseId);
+}
- if (!TransactionIdEquals(pgStatDBHashXact, topXid))
- {
- Assert(!pgStatRunningInCollector);
- pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId);
- pgStatDBHashXact = topXid;
- }
- }
+
+/* ----------
+ * pgstat_setup_memcxt() -
+ *
+ * Create pgStatLocalContext, if not already done.
+ * ----------
+ */
+static void
+pgstat_setup_memcxt(void)
+{
+ if (!pgStatLocalContext)
+ pgStatLocalContext = AllocSetContextCreate(TopMemoryContext,
+ "Statistics snapshot",
+ ALLOCSET_SMALL_MINSIZE,
+ ALLOCSET_SMALL_INITSIZE,
+ ALLOCSET_SMALL_MAXSIZE);
}
+
+/* ----------
+ * pgstat_clear_snapshot() -
+ *
+ * Discard any data collected in the current transaction. Any subsequent
+ * request will cause new snapshots to be read.
+ *
+ * This is also invoked during transaction commit or abort to discard
+ * the no-longer-wanted snapshot.
+ * ----------
+ */
+void
+pgstat_clear_snapshot(void)
+{
+ /* In an autovacuum process we keep the stats forever */
+ if (IsAutoVacuumProcess())
+ return;
+
+ /* Release memory, if any was allocated */
+ if (pgStatLocalContext)
+ MemoryContextDelete(pgStatLocalContext);
+
+ /* Reset variables */
+ pgStatLocalContext = NULL;
+ pgStatDBHash = NULL;
+ localBackendStatusTable = NULL;
+ localNumBackends = 0;
+}
+
+
/* ----------
* pgstat_recv_tabstat() -
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.37 2007/01/05 22:19:41 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.38 2007/02/07 23:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
-extern Datum pg_stat_reset(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_backend_userid(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS);
+extern Datum pg_stat_clear_snapshot(PG_FUNCTION_ARGS);
+extern Datum pg_stat_reset(PG_FUNCTION_ARGS);
+
Datum
pg_stat_get_numscans(PG_FUNCTION_ARGS)
PG_RETURN_INT32(MyProcPid);
}
-/*
- * Built-in function for resetting the counters
- */
-Datum
-pg_stat_reset(PG_FUNCTION_ARGS)
-{
- pgstat_reset_counters();
-
- PG_RETURN_BOOL(true);
-}
Datum
pg_stat_get_backend_pid(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+
+
+/* Discard the active statistics snapshot */
+Datum
+pg_stat_clear_snapshot(PG_FUNCTION_ARGS)
+{
+ pgstat_clear_snapshot();
+
+ PG_RETURN_VOID();
+}
+
+
+/* Reset all counters for the current database */
+Datum
+pg_stat_reset(PG_FUNCTION_ARGS)
+{
+ pgstat_reset_counters();
+
+ PG_RETURN_VOID();
+}
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.381 2007/02/06 02:59:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.382 2007/02/07 23:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200702051
+#define CATALOG_VERSION_NO 200702071
#endif
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.442 2007/02/03 14:06:55 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.443 2007/02/07 23:11:30 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
DESCR("Statistics: Currently active backend IDs");
DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 f f t f s 0 23 "" _null_ _null_ _null_ pg_backend_pid - _null_ ));
DESCR("Statistics: Current backend PID");
-DATA(insert OID = 2274 ( pg_stat_reset PGNSP PGUID 12 1 0 f f f f v 0 16 "" _null_ _null_ _null_ pg_stat_reset - _null_ ));
-DESCR("Statistics: Reset collected statistics");
DATA(insert OID = 1937 ( pg_stat_get_backend_pid PGNSP PGUID 12 1 0 f f t f s 1 23 "23" _null_ _null_ _null_ pg_stat_get_backend_pid - _null_ ));
DESCR("Statistics: PID of backend");
DATA(insert OID = 1938 ( pg_stat_get_backend_dbid PGNSP PGUID 12 1 0 f f t f s 1 26 "23" _null_ _null_ _null_ pg_stat_get_backend_dbid - _null_ ));
DESCR("Statistics: Blocks fetched for database");
DATA(insert OID = 1945 ( pg_stat_get_db_blocks_hit PGNSP PGUID 12 1 0 f f t f s 1 20 "26" _null_ _null_ _null_ pg_stat_get_db_blocks_hit - _null_ ));
DESCR("Statistics: Blocks found in cache for database");
+DATA(insert OID = 2230 ( pg_stat_clear_snapshot PGNSP PGUID 12 1 0 f f f f v 0 2278 "" _null_ _null_ _null_ pg_stat_clear_snapshot - _null_ ));
+DESCR("Statistics: Discard current transaction's statistics snapshot");
+DATA(insert OID = 2274 ( pg_stat_reset PGNSP PGUID 12 1 0 f f f f v 0 2278 "" _null_ _null_ _null_ pg_stat_reset - _null_ ));
+DESCR("Statistics: Reset collected statistics for current database");
DATA(insert OID = 1946 ( encode PGNSP PGUID 12 1 0 f f t f i 2 25 "17 25" _null_ _null_ _null_ binary_encode - _null_ ));
DESCR("Convert bytea value into some ascii-only text string");
*
* Copyright (c) 2001-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.52 2007/01/05 22:19:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.53 2007/02/07 23:11:30 tgl Exp $
* ----------
*/
#ifndef PGSTAT_H
extern void pgstat_vacuum_tabstat(void);
extern void pgstat_drop_relation(Oid relid);
+extern void pgstat_clear_snapshot(void);
extern void pgstat_reset_counters(void);
extern void pgstat_report_autovac(Oid dboid);
FROM pg_catalog.pg_stat_user_tables AS t,
pg_catalog.pg_statio_user_tables AS b
WHERE t.relname='tenk2' AND b.relname='tenk2';
--- enable statistics
-SET stats_block_level = on;
-SET stats_row_level = on;
--- do a seqscan
-SELECT count(*) FROM tenk2;
- count
--------
- 10000
-(1 row)
-
--- do an indexscan
-SELECT count(*) FROM tenk2 WHERE unique1 = 1;
- count
--------
- 1
-(1 row)
-
--- All of the thrashing here is to wait for the stats collector to update,
--- without waiting too long (in fact, we'd like to try to measure how long
--- we wait). Watching for change in the stats themselves wouldn't work
--- because the backend only reads them once per transaction. The stats file
--- mod timestamp isn't too helpful because it may have resolution of only one
--- second, or even worse. So, we touch a new table and then watch for change
--- in the size of the stats file. Ugh.
--- save current stats-file size
-CREATE TEMP TABLE prevfilesize AS
- SELECT size FROM pg_stat_file('global/pgstat.stat');
--- make and touch a previously nonexistent table
-CREATE TABLE stats_hack (f1 int);
-SELECT * FROM stats_hack;
- f1
-----
-(0 rows)
-
--- wait for stats collector to update
+-- function to wait for counters to advance
create function wait_for_stats() returns void as $$
declare
start_time timestamptz := clock_timestamp();
- oldsize bigint;
- newsize bigint;
+ updated bool;
begin
- -- fetch previous stats-file size
- select size into oldsize from prevfilesize;
-
-- we don't want to wait forever; loop will exit after 30 seconds
for i in 1 .. 300 loop
- -- look for update of stats file
- select size into newsize from pg_stat_file('global/pgstat.stat');
+ -- check to see if indexscan has been sensed
+ SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated
+ FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
+ WHERE st.relname='tenk2' AND cl.relname='tenk2';
- exit when newsize != oldsize;
+ exit when updated;
-- wait a little
perform pg_sleep(0.1);
+ -- reset stats snapshot so we can test again
+ perform pg_stat_clear_snapshot();
+
end loop;
-- report time waited in postmaster log (where it won't change test output)
extract(epoch from clock_timestamp() - start_time);
end
$$ language plpgsql;
+-- enable statistics
+SET stats_block_level = on;
+SET stats_row_level = on;
+-- do a seqscan
+SELECT count(*) FROM tenk2;
+ count
+-------
+ 10000
+(1 row)
+
+-- do an indexscan
+SELECT count(*) FROM tenk2 WHERE unique1 = 1;
+ count
+-------
+ 1
+(1 row)
+
+-- wait for stats collector to update
SELECT wait_for_stats();
wait_for_stats
----------------
(1 row)
-DROP TABLE stats_hack;
-- check effects
SELECT st.seq_scan >= pr.seq_scan + 1,
st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
pg_catalog.pg_statio_user_tables AS b
WHERE t.relname='tenk2' AND b.relname='tenk2';
--- enable statistics
-SET stats_block_level = on;
-SET stats_row_level = on;
-
--- do a seqscan
-SELECT count(*) FROM tenk2;
--- do an indexscan
-SELECT count(*) FROM tenk2 WHERE unique1 = 1;
-
--- All of the thrashing here is to wait for the stats collector to update,
--- without waiting too long (in fact, we'd like to try to measure how long
--- we wait). Watching for change in the stats themselves wouldn't work
--- because the backend only reads them once per transaction. The stats file
--- mod timestamp isn't too helpful because it may have resolution of only one
--- second, or even worse. So, we touch a new table and then watch for change
--- in the size of the stats file. Ugh.
-
--- save current stats-file size
-CREATE TEMP TABLE prevfilesize AS
- SELECT size FROM pg_stat_file('global/pgstat.stat');
-
--- make and touch a previously nonexistent table
-CREATE TABLE stats_hack (f1 int);
-SELECT * FROM stats_hack;
-
--- wait for stats collector to update
+-- function to wait for counters to advance
create function wait_for_stats() returns void as $$
declare
start_time timestamptz := clock_timestamp();
- oldsize bigint;
- newsize bigint;
+ updated bool;
begin
- -- fetch previous stats-file size
- select size into oldsize from prevfilesize;
-
-- we don't want to wait forever; loop will exit after 30 seconds
for i in 1 .. 300 loop
- -- look for update of stats file
- select size into newsize from pg_stat_file('global/pgstat.stat');
+ -- check to see if indexscan has been sensed
+ SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated
+ FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
+ WHERE st.relname='tenk2' AND cl.relname='tenk2';
- exit when newsize != oldsize;
+ exit when updated;
-- wait a little
perform pg_sleep(0.1);
+ -- reset stats snapshot so we can test again
+ perform pg_stat_clear_snapshot();
+
end loop;
-- report time waited in postmaster log (where it won't change test output)
end
$$ language plpgsql;
-SELECT wait_for_stats();
+-- enable statistics
+SET stats_block_level = on;
+SET stats_row_level = on;
-DROP TABLE stats_hack;
+-- do a seqscan
+SELECT count(*) FROM tenk2;
+-- do an indexscan
+SELECT count(*) FROM tenk2 WHERE unique1 = 1;
+
+-- wait for stats collector to update
+SELECT wait_for_stats();
-- check effects
SELECT st.seq_scan >= pr.seq_scan + 1,