*
* Copyright (c) 2001-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.194 2009/12/27 19:40:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.195 2009/12/30 20:32:14 tgl Exp $
* ----------
*/
#include "postgres.h"
typedef struct TwoPhasePgStatRecord
{
PgStat_Counter tuples_inserted; /* tuples inserted in xact */
+ PgStat_Counter tuples_updated; /* tuples updated in xact */
PgStat_Counter tuples_deleted; /* tuples deleted in xact */
Oid t_id; /* table's OID */
bool t_shared; /* is it a shared catalog? */
* ---------
*/
void
-pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
- bool analyze, PgStat_Counter tuples)
+pgstat_report_vacuum(Oid tableoid, bool shared, bool adopt_counts,
+ PgStat_Counter tuples)
{
PgStat_MsgVacuum msg;
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM);
msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
msg.m_tableoid = tableoid;
- msg.m_scanned_all = scanned_all;
- msg.m_analyze = analyze;
- msg.m_autovacuum = IsAutoVacuumWorkerProcess(); /* is this autovacuum? */
+ msg.m_adopt_counts = adopt_counts;
+ msg.m_autovacuum = IsAutoVacuumWorkerProcess();
msg.m_vacuumtime = GetCurrentTimestamp();
msg.m_tuples = tuples;
pgstat_send(&msg, sizeof(msg));
* --------
*/
void
-pgstat_report_analyze(Relation rel, PgStat_Counter livetuples,
- PgStat_Counter deadtuples)
+pgstat_report_analyze(Relation rel, bool adopt_counts,
+ PgStat_Counter livetuples, PgStat_Counter deadtuples)
{
PgStat_MsgAnalyze msg;
for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
{
livetuples -= trans->tuples_inserted - trans->tuples_deleted;
- deadtuples -= trans->tuples_deleted;
+ deadtuples -= trans->tuples_updated + trans->tuples_deleted;
}
/* count stuff inserted by already-aborted subxacts, too */
- deadtuples -= rel->pgstat_info->t_counts.t_new_dead_tuples;
+ deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
/* Since ANALYZE's counts are estimates, we could have underflowed */
livetuples = Max(livetuples, 0);
deadtuples = Max(deadtuples, 0);
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
msg.m_tableoid = RelationGetRelid(rel);
- msg.m_autovacuum = IsAutoVacuumWorkerProcess(); /* is this autovacuum? */
+ msg.m_adopt_counts = adopt_counts;
+ msg.m_autovacuum = IsAutoVacuumWorkerProcess();
msg.m_analyzetime = GetCurrentTimestamp();
msg.m_live_tuples = livetuples;
msg.m_dead_tuples = deadtuples;
if (pgstat_track_counts && pgstat_info != NULL)
{
+ /* We have to log the effect at the proper transactional level */
int nest_level = GetCurrentTransactionNestLevel();
- /* t_tuples_inserted is nontransactional, so just advance it */
- pgstat_info->t_counts.t_tuples_inserted++;
-
- /* We have to log the transactional effect at the proper level */
if (pgstat_info->trans == NULL ||
pgstat_info->trans->nest_level != nest_level)
add_tabstat_xact_level(pgstat_info, nest_level);
if (pgstat_track_counts && pgstat_info != NULL)
{
+ /* We have to log the effect at the proper transactional level */
int nest_level = GetCurrentTransactionNestLevel();
- /* t_tuples_updated is nontransactional, so just advance it */
- pgstat_info->t_counts.t_tuples_updated++;
- /* ditto for the hot_update counter */
- if (hot)
- pgstat_info->t_counts.t_tuples_hot_updated++;
-
- /* We have to log the transactional effect at the proper level */
if (pgstat_info->trans == NULL ||
pgstat_info->trans->nest_level != nest_level)
add_tabstat_xact_level(pgstat_info, nest_level);
- /* An UPDATE both inserts a new tuple and deletes the old */
- pgstat_info->trans->tuples_inserted++;
- pgstat_info->trans->tuples_deleted++;
+ pgstat_info->trans->tuples_updated++;
+
+ /* t_tuples_hot_updated is nontransactional, so just advance it */
+ if (hot)
+ pgstat_info->t_counts.t_tuples_hot_updated++;
}
}
if (pgstat_track_counts && pgstat_info != NULL)
{
+ /* We have to log the effect at the proper transactional level */
int nest_level = GetCurrentTransactionNestLevel();
- /* t_tuples_deleted is nontransactional, so just advance it */
- pgstat_info->t_counts.t_tuples_deleted++;
-
- /* We have to log the transactional effect at the proper level */
if (pgstat_info->trans == NULL ||
pgstat_info->trans->nest_level != nest_level)
add_tabstat_xact_level(pgstat_info, nest_level);
* pgstat_update_heap_dead_tuples - update dead-tuples count
*
* The semantics of this are that we are reporting the nontransactional
- * recovery of "delta" dead tuples; so t_new_dead_tuples decreases
+ * recovery of "delta" dead tuples; so t_delta_dead_tuples decreases
* rather than increasing, and the change goes straight into the per-table
* counter, not into transactional state.
*/
PgStat_TableStatus *pgstat_info = rel->pgstat_info;
if (pgstat_track_counts && pgstat_info != NULL)
- pgstat_info->t_counts.t_new_dead_tuples -= delta;
+ pgstat_info->t_counts.t_delta_dead_tuples -= delta;
}
Assert(trans->upper == NULL);
tabstat = trans->parent;
Assert(tabstat->trans == trans);
+ /* count attempted actions regardless of commit/abort */
+ tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
+ tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
+ tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
if (isCommit)
{
- tabstat->t_counts.t_new_live_tuples +=
+ /* insert adds a live tuple, delete removes one */
+ tabstat->t_counts.t_delta_live_tuples +=
trans->tuples_inserted - trans->tuples_deleted;
- tabstat->t_counts.t_new_dead_tuples += trans->tuples_deleted;
+ /* update and delete each create a dead tuple */
+ tabstat->t_counts.t_delta_dead_tuples +=
+ trans->tuples_updated + trans->tuples_deleted;
+ /* insert, update, delete each count as one change event */
+ tabstat->t_counts.t_changed_tuples +=
+ trans->tuples_inserted + trans->tuples_updated +
+ trans->tuples_deleted;
}
else
{
/* inserted tuples are dead, deleted tuples are unaffected */
- tabstat->t_counts.t_new_dead_tuples += trans->tuples_inserted;
+ tabstat->t_counts.t_delta_dead_tuples +=
+ trans->tuples_inserted + trans->tuples_updated;
+ /* an aborted xact generates no changed_tuple events */
}
tabstat->trans = NULL;
}
if (trans->upper && trans->upper->nest_level == nestDepth - 1)
{
trans->upper->tuples_inserted += trans->tuples_inserted;
+ trans->upper->tuples_updated += trans->tuples_updated;
trans->upper->tuples_deleted += trans->tuples_deleted;
tabstat->trans = trans->upper;
pfree(trans);
else
{
/*
- * On abort, inserted tuples are dead (and can be bounced out
- * to the top-level tabstat), deleted tuples are unaffected
+ * On abort, update top-level tabstat counts, then forget
+ * the subtransaction
*/
- tabstat->t_counts.t_new_dead_tuples += trans->tuples_inserted;
+
+ /* count attempted actions regardless of commit/abort */
+ tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
+ tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
+ tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
+ /* inserted tuples are dead, deleted tuples are unaffected */
+ tabstat->t_counts.t_delta_dead_tuples +=
+ trans->tuples_inserted + trans->tuples_updated;
tabstat->trans = trans->upper;
pfree(trans);
}
Assert(tabstat->trans == trans);
record.tuples_inserted = trans->tuples_inserted;
+ record.tuples_updated = trans->tuples_updated;
record.tuples_deleted = trans->tuples_deleted;
record.t_id = tabstat->t_id;
record.t_shared = tabstat->t_shared;
/* Find or create a tabstat entry for the rel */
pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
- pgstat_info->t_counts.t_new_live_tuples +=
+ /* Same math as in AtEOXact_PgStat, commit case */
+ pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+ pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
+ pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+ pgstat_info->t_counts.t_delta_live_tuples +=
rec->tuples_inserted - rec->tuples_deleted;
- pgstat_info->t_counts.t_new_dead_tuples += rec->tuples_deleted;
+ pgstat_info->t_counts.t_delta_dead_tuples +=
+ rec->tuples_updated + rec->tuples_deleted;
+ pgstat_info->t_counts.t_changed_tuples +=
+ rec->tuples_inserted + rec->tuples_updated +
+ rec->tuples_deleted;
}
/*
/* Find or create a tabstat entry for the rel */
pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
- /* inserted tuples are dead, deleted tuples are no-ops */
- pgstat_info->t_counts.t_new_dead_tuples += rec->tuples_inserted;
+ /* Same math as in AtEOXact_PgStat, abort case */
+ pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+ pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
+ pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+ pgstat_info->t_counts.t_delta_dead_tuples +=
+ rec->tuples_inserted + rec->tuples_updated;
}
result->tuples_hot_updated = 0;
result->n_live_tuples = 0;
result->n_dead_tuples = 0;
- result->last_anl_tuples = 0;
+ result->changes_since_analyze = 0;
result->blocks_fetched = 0;
result->blocks_hit = 0;
static void
pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
{
- PgStat_TableEntry *tabmsg = &(msg->m_entry[0]);
PgStat_StatDBEntry *dbentry;
PgStat_StatTabEntry *tabentry;
int i;
*/
for (i = 0; i < msg->m_nentries; i++)
{
+ PgStat_TableEntry *tabmsg = &(msg->m_entry[i]);
+
tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
- (void *) &(tabmsg[i].t_id),
+ (void *) &(tabmsg->t_id),
HASH_ENTER, &found);
if (!found)
* If it's a new table entry, initialize counters to the values we
* just got.
*/
- tabentry->numscans = tabmsg[i].t_counts.t_numscans;
- tabentry->tuples_returned = tabmsg[i].t_counts.t_tuples_returned;
- tabentry->tuples_fetched = tabmsg[i].t_counts.t_tuples_fetched;
- tabentry->tuples_inserted = tabmsg[i].t_counts.t_tuples_inserted;
- tabentry->tuples_updated = tabmsg[i].t_counts.t_tuples_updated;
- tabentry->tuples_deleted = tabmsg[i].t_counts.t_tuples_deleted;
- tabentry->tuples_hot_updated = tabmsg[i].t_counts.t_tuples_hot_updated;
- tabentry->n_live_tuples = tabmsg[i].t_counts.t_new_live_tuples;
- tabentry->n_dead_tuples = tabmsg[i].t_counts.t_new_dead_tuples;
- tabentry->last_anl_tuples = 0;
- tabentry->blocks_fetched = tabmsg[i].t_counts.t_blocks_fetched;
- tabentry->blocks_hit = tabmsg[i].t_counts.t_blocks_hit;
+ tabentry->numscans = tabmsg->t_counts.t_numscans;
+ tabentry->tuples_returned = tabmsg->t_counts.t_tuples_returned;
+ tabentry->tuples_fetched = tabmsg->t_counts.t_tuples_fetched;
+ tabentry->tuples_inserted = tabmsg->t_counts.t_tuples_inserted;
+ tabentry->tuples_updated = tabmsg->t_counts.t_tuples_updated;
+ tabentry->tuples_deleted = tabmsg->t_counts.t_tuples_deleted;
+ tabentry->tuples_hot_updated = tabmsg->t_counts.t_tuples_hot_updated;
+ tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples;
+ tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples;
+ tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples;
+ tabentry->blocks_fetched = tabmsg->t_counts.t_blocks_fetched;
+ tabentry->blocks_hit = tabmsg->t_counts.t_blocks_hit;
tabentry->vacuum_timestamp = 0;
tabentry->autovac_vacuum_timestamp = 0;
/*
* Otherwise add the values to the existing entry.
*/
- tabentry->numscans += tabmsg[i].t_counts.t_numscans;
- tabentry->tuples_returned += tabmsg[i].t_counts.t_tuples_returned;
- tabentry->tuples_fetched += tabmsg[i].t_counts.t_tuples_fetched;
- tabentry->tuples_inserted += tabmsg[i].t_counts.t_tuples_inserted;
- tabentry->tuples_updated += tabmsg[i].t_counts.t_tuples_updated;
- tabentry->tuples_deleted += tabmsg[i].t_counts.t_tuples_deleted;
- tabentry->tuples_hot_updated += tabmsg[i].t_counts.t_tuples_hot_updated;
- tabentry->n_live_tuples += tabmsg[i].t_counts.t_new_live_tuples;
- tabentry->n_dead_tuples += tabmsg[i].t_counts.t_new_dead_tuples;
- tabentry->blocks_fetched += tabmsg[i].t_counts.t_blocks_fetched;
- tabentry->blocks_hit += tabmsg[i].t_counts.t_blocks_hit;
+ tabentry->numscans += tabmsg->t_counts.t_numscans;
+ tabentry->tuples_returned += tabmsg->t_counts.t_tuples_returned;
+ tabentry->tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
+ tabentry->tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
+ tabentry->tuples_updated += tabmsg->t_counts.t_tuples_updated;
+ tabentry->tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
+ tabentry->tuples_hot_updated += tabmsg->t_counts.t_tuples_hot_updated;
+ tabentry->n_live_tuples += tabmsg->t_counts.t_delta_live_tuples;
+ tabentry->n_dead_tuples += tabmsg->t_counts.t_delta_dead_tuples;
+ tabentry->changes_since_analyze += tabmsg->t_counts.t_changed_tuples;
+ tabentry->blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
+ tabentry->blocks_hit += tabmsg->t_counts.t_blocks_hit;
}
- /* Clamp n_live_tuples in case of negative new_live_tuples */
+ /* Clamp n_live_tuples in case of negative delta_live_tuples */
tabentry->n_live_tuples = Max(tabentry->n_live_tuples, 0);
/* Likewise for n_dead_tuples */
tabentry->n_dead_tuples = Max(tabentry->n_dead_tuples, 0);
/*
* Add per-table stats to the per-database entry, too.
*/
- dbentry->n_tuples_returned += tabmsg[i].t_counts.t_tuples_returned;
- dbentry->n_tuples_fetched += tabmsg[i].t_counts.t_tuples_fetched;
- dbentry->n_tuples_inserted += tabmsg[i].t_counts.t_tuples_inserted;
- dbentry->n_tuples_updated += tabmsg[i].t_counts.t_tuples_updated;
- dbentry->n_tuples_deleted += tabmsg[i].t_counts.t_tuples_deleted;
- dbentry->n_blocks_fetched += tabmsg[i].t_counts.t_blocks_fetched;
- dbentry->n_blocks_hit += tabmsg[i].t_counts.t_blocks_hit;
+ dbentry->n_tuples_returned += tabmsg->t_counts.t_tuples_returned;
+ dbentry->n_tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
+ dbentry->n_tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
+ dbentry->n_tuples_updated += tabmsg->t_counts.t_tuples_updated;
+ dbentry->n_tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
+ dbentry->n_blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
+ dbentry->n_blocks_hit += tabmsg->t_counts.t_blocks_hit;
}
}
tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
- if (msg->m_autovacuum)
- tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
- else
- tabentry->vacuum_timestamp = msg->m_vacuumtime;
- if (msg->m_scanned_all)
+ if (msg->m_adopt_counts)
tabentry->n_live_tuples = msg->m_tuples;
/* Resetting dead_tuples to 0 is an approximation ... */
tabentry->n_dead_tuples = 0;
- if (msg->m_analyze)
- {
- if (msg->m_scanned_all)
- tabentry->last_anl_tuples = msg->m_tuples;
- else
- {
- /* last_anl_tuples must never exceed n_live_tuples+n_dead_tuples */
- tabentry->last_anl_tuples = Min(tabentry->last_anl_tuples,
- tabentry->n_live_tuples);
- }
- if (msg->m_autovacuum)
- tabentry->autovac_analyze_timestamp = msg->m_vacuumtime;
- else
- tabentry->analyze_timestamp = msg->m_vacuumtime;
- }
+ if (msg->m_autovacuum)
+ tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
else
- {
- /* last_anl_tuples must never exceed n_live_tuples+n_dead_tuples */
- tabentry->last_anl_tuples = Min(tabentry->last_anl_tuples,
- tabentry->n_live_tuples);
- }
+ tabentry->vacuum_timestamp = msg->m_vacuumtime;
}
/* ----------
tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
+ if (msg->m_adopt_counts)
+ {
+ tabentry->n_live_tuples = msg->m_live_tuples;
+ tabentry->n_dead_tuples = msg->m_dead_tuples;
+ }
+
+ /*
+ * We reset changes_since_analyze to zero, forgetting any changes that
+ * occurred while the ANALYZE was in progress.
+ */
+ tabentry->changes_since_analyze = 0;
+
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;
}
*
* Copyright (c) 2001-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.84 2009/11/28 23:38:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/pgstat.h,v 1.85 2009/12/30 20:32:14 tgl Exp $
* ----------
*/
#ifndef PGSTAT_H
* fetched by heap_fetch under the control of simple indexscans for this index.
*
* tuples_inserted/updated/deleted/hot_updated count attempted actions,
- * regardless of whether the transaction committed. new_live_tuples and
- * new_dead_tuples are properly adjusted depending on commit or abort.
- * Note that new_live_tuples and new_dead_tuples can be negative!
+ * regardless of whether the transaction committed. delta_live_tuples,
+ * delta_dead_tuples, and changed_tuples are set depending on commit or abort.
+ * Note that delta_live_tuples and delta_dead_tuples can be negative!
* ----------
*/
typedef struct PgStat_TableCounts
PgStat_Counter t_tuples_deleted;
PgStat_Counter t_tuples_hot_updated;
- PgStat_Counter t_new_live_tuples;
- PgStat_Counter t_new_dead_tuples;
+ PgStat_Counter t_delta_live_tuples;
+ PgStat_Counter t_delta_dead_tuples;
+ PgStat_Counter t_changed_tuples;
PgStat_Counter t_blocks_fetched;
PgStat_Counter t_blocks_hit;
/* ----------
* PgStat_TableStatus Per-table status within a backend
*
- * Most of the event counters are nontransactional, ie, we count events
+ * Many of the event counters are nontransactional, ie, we count events
* in committed and aborted transactions alike. For these, we just count
- * directly in the PgStat_TableStatus. However, new_live_tuples and
- * new_dead_tuples must be derived from tuple insertion and deletion counts
+ * directly in the PgStat_TableStatus. However, delta_live_tuples,
+ * delta_dead_tuples, and changed_tuples must be derived from event counts
* with awareness of whether the transaction or subtransaction committed or
* aborted. Hence, we also keep a stack of per-(sub)transaction status
* records for every table modified in the current transaction. At commit
- * or abort, we propagate tuples_inserted and tuples_deleted up to the
+ * or abort, we propagate tuples_inserted/updated/deleted up to the
* parent subtransaction level, or out to the parent PgStat_TableStatus,
* as appropriate.
* ----------
typedef struct PgStat_TableXactStatus
{
PgStat_Counter tuples_inserted; /* tuples inserted in (sub)xact */
+ PgStat_Counter tuples_updated; /* tuples updated in (sub)xact */
PgStat_Counter tuples_deleted; /* tuples deleted in (sub)xact */
int nest_level; /* subtransaction nest level */
/* links to other structs for same relation: */
/* ----------
* PgStat_MsgVacuum Sent by the backend or autovacuum daemon
- * after VACUUM or VACUUM ANALYZE
+ * after VACUUM
* ----------
*/
typedef struct PgStat_MsgVacuum
PgStat_MsgHdr m_hdr;
Oid m_databaseid;
Oid m_tableoid;
- bool m_analyze;
+ bool m_adopt_counts;
bool m_autovacuum;
- bool m_scanned_all;
TimestampTz m_vacuumtime;
PgStat_Counter m_tuples;
} PgStat_MsgVacuum;
PgStat_MsgHdr m_hdr;
Oid m_databaseid;
Oid m_tableoid;
+ bool m_adopt_counts;
bool m_autovacuum;
TimestampTz m_analyzetime;
PgStat_Counter m_live_tuples;
PgStat_Counter n_live_tuples;
PgStat_Counter n_dead_tuples;
- PgStat_Counter last_anl_tuples;
+ PgStat_Counter changes_since_analyze;
PgStat_Counter blocks_fetched;
PgStat_Counter blocks_hit;
extern void pgstat_reset_counters(void);
extern void pgstat_report_autovac(Oid dboid);
-extern void pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
- bool analyze, PgStat_Counter tuples);
-extern void pgstat_report_analyze(Relation rel,
- PgStat_Counter livetuples,
- PgStat_Counter deadtuples);
+extern void pgstat_report_vacuum(Oid tableoid, bool shared, bool adopt_counts,
+ PgStat_Counter tuples);
+extern void pgstat_report_analyze(Relation rel, bool adopt_counts,
+ PgStat_Counter livetuples, PgStat_Counter deadtuples);
extern void pgstat_initialize(void);
extern void pgstat_bestart(void);