* - Add a pgstat config column to pg_database, so this
* entire thing can be enabled/disabled on a per db basis.
*
- * Copyright (c) 2001-2009, PostgreSQL Global Development Group
+ * Copyright (c) 2001-2011, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.192 2009/10/02 22:49:50 tgl Exp $
+ * src/backend/postmaster/pgstat.c
* ----------
*/
#include "postgres.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
+#include "storage/procsignal.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
* Local data
* ----------
*/
-NON_EXEC_STATIC int pgStatSock = -1;
+NON_EXEC_STATIC pgsocket pgStatSock = PGINVALID_SOCKET;
static struct sockaddr_storage pgStatAddr;
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? */
static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
static PgStat_StatTabEntry *pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry,
- Oid tableoid, bool create);
+ Oid tableoid, bool create);
static void pgstat_write_statsfile(bool permanent);
static HTAB *pgstat_read_statsfile(Oid onlydb, bool permanent);
static void backend_read_statsfile(void);
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_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len);
+static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *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);
static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
+static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
/* ------------------------------------------------------------
/*
* Create the socket.
*/
- if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) < 0)
+ if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) == PGINVALID_SOCKET)
{
ereport(LOG,
(errcode_for_socket_access(),
(errcode_for_socket_access(),
errmsg("could not bind socket for statistics collector: %m")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
(errcode_for_socket_access(),
errmsg("could not get address of socket for statistics collector: %m")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
(errcode_for_socket_access(),
errmsg("could not connect socket for statistics collector: %m")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
(errcode_for_socket_access(),
errmsg("could not send test message on socket for statistics collector: %m")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
for (;;) /* need a loop to handle EINTR */
{
FD_ZERO(&rset);
- FD_SET (pgStatSock, &rset);
+ FD_SET(pgStatSock, &rset);
tv.tv_sec = 0;
tv.tv_usec = 500000;
(errcode_for_socket_access(),
errmsg("select() failed in statistics collector: %m")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
if (sel_res == 0 || !FD_ISSET(pgStatSock, &rset))
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("test message did not get through on socket for statistics collector")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
(errcode_for_socket_access(),
errmsg("could not receive test message on socket for statistics collector: %m")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("incorrect test message transmission on socket for statistics collector")));
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
continue;
}
}
/* Did we find a working address? */
- if (!addr || pgStatSock < 0)
+ if (!addr || pgStatSock == PGINVALID_SOCKET)
goto startup_failed;
/*
if (addrs)
pg_freeaddrinfo_all(hints.ai_family, addrs);
- if (pgStatSock >= 0)
+ if (pgStatSock != PGINVALID_SOCKET)
closesocket(pgStatSock);
- pgStatSock = -1;
+ pgStatSock = PGINVALID_SOCKET;
/*
* Adjust GUC variables to suppress useless activity, and for debugging
* Check that the socket is there, else pgstat_init failed and we can do
* nothing useful.
*/
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return 0;
/*
int len;
/* It's unlikely we'd get here with no socket, but maybe not impossible */
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
/*
PgStat_StatFuncEntry *funcentry;
int len;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
/*
*
* Collect the OIDs of all objects listed in the specified system catalog
* into a temporary hash table. Caller should hash_destroy the result
- * when done with it.
+ * when done with it. (However, we make the table in CurrentMemoryContext
+ * so that it will be freed properly in event of an error.)
* ----------
*/
static HTAB *
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(Oid);
hash_ctl.hash = oid_hash;
+ hash_ctl.hcxt = CurrentMemoryContext;
htab = hash_create("Temporary table of OIDs",
PGSTAT_TAB_HASH_SIZE,
&hash_ctl,
- HASH_ELEM | HASH_FUNCTION);
+ HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
rel = heap_open(catalogid, AccessShareLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
{
PgStat_MsgDropdb msg;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DROPDB);
PgStat_MsgTabpurge msg;
int len;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
msg.m_tableid[0] = relid;
{
PgStat_MsgResetcounter msg;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
if (!superuser())
pgstat_send(&msg, sizeof(msg));
}
+/* ----------
+ * pgstat_reset_shared_counters() -
+ *
+ * Tell the statistics collector to reset cluster-wide shared counters.
+ * ----------
+ */
+void
+pgstat_reset_shared_counters(const char *target)
+{
+ PgStat_MsgResetsharedcounter msg;
+
+ if (pgStatSock == PGINVALID_SOCKET)
+ return;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to reset statistics counters")));
+
+ if (strcmp(target, "bgwriter") == 0)
+ msg.m_resettarget = RESET_BGWRITER;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized reset target: \"%s\"", target),
+ errhint("Target must be \"bgwriter\".")));
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
+ pgstat_send(&msg, sizeof(msg));
+}
+
+/* ----------
+ * pgstat_reset_single_counter() -
+ *
+ * Tell the statistics collector to reset a single counter.
+ * ----------
+ */
+void
+pgstat_reset_single_counter(Oid objoid, PgStat_Single_Reset_Type type)
+{
+ PgStat_MsgResetsinglecounter msg;
+
+ if (pgStatSock == PGINVALID_SOCKET)
+ return;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to reset statistics counters")));
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSINGLECOUNTER);
+ msg.m_databaseid = MyDatabaseId;
+ msg.m_resettype = type;
+ msg.m_objectid = objoid;
+
+ pgstat_send(&msg, sizeof(msg));
+}
/* ----------
* pgstat_report_autovac() -
{
PgStat_MsgAutovacStart msg;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START);
* ---------
*/
void
-pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
- bool analyze, PgStat_Counter tuples)
+pgstat_report_vacuum(Oid tableoid, bool shared, PgStat_Counter tuples)
{
PgStat_MsgVacuum msg;
- if (pgStatSock < 0 || !pgstat_track_counts)
+ if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
return;
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_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,
+ PgStat_Counter livetuples, PgStat_Counter deadtuples)
{
PgStat_MsgAnalyze msg;
- if (pgStatSock < 0 || !pgstat_track_counts)
+ if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
return;
/*
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_autovacuum = IsAutoVacuumWorkerProcess();
msg.m_analyzetime = GetCurrentTimestamp();
msg.m_live_tuples = livetuples;
msg.m_dead_tuples = deadtuples;
pgstat_send(&msg, sizeof(msg));
}
+/* --------
+ * pgstat_report_recovery_conflict() -
+ *
+ * Tell the collector about a Hot Standby recovery conflict.
+ * --------
+ */
+void
+pgstat_report_recovery_conflict(int reason)
+{
+ PgStat_MsgRecoveryConflict msg;
+
+ if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
+ return;
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RECOVERYCONFLICT);
+ msg.m_databaseid = MyDatabaseId;
+ msg.m_reason = reason;
+ pgstat_send(&msg, sizeof(msg));
+}
/* ----------
* pgstat_ping() -
{
PgStat_MsgDummy msg;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DUMMY);
INSTR_TIME_SET_CURRENT(fcu->f_start);
}
+/*
+ * find_funcstat_entry - find any existing PgStat_BackendFunctionEntry entry
+ * for specified function
+ *
+ * If no entry, return NULL, don't create a new one
+ */
+PgStat_BackendFunctionEntry *
+find_funcstat_entry(Oid func_id)
+{
+ if (pgStatFunctions == NULL)
+ return NULL;
+
+ return (PgStat_BackendFunctionEntry *) hash_search(pgStatFunctions,
+ (void *) &func_id,
+ HASH_FIND, NULL);
+}
+
/*
* Calculate function call usage and update stat counters.
* Called by the executor after invoking a function.
return;
}
- if (pgStatSock < 0 || !pgstat_track_counts)
+ if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
{
/* We're not counting at all */
rel->pgstat_info = NULL;
return entry;
}
+/*
+ * find_tabstat_entry - find any existing PgStat_TableStatus entry for rel
+ *
+ * If no entry, return NULL, don't create a new one
+ */
+PgStat_TableStatus *
+find_tabstat_entry(Oid rel_id)
+{
+ PgStat_TableStatus *entry;
+ TabStatusArray *tsa;
+ int i;
+
+ for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
+ {
+ for (i = 0; i < tsa->tsa_used; i++)
+ {
+ entry = &tsa->tsa_entries[i];
+ if (entry->t_id == rel_id)
+ return entry;
+ }
+ }
+
+ /* Not present */
+ return NULL;
+}
+
/*
* get_tabstat_stack_level - add a new (sub)transaction stack entry if needed
*/
{
PgStat_TableStatus *pgstat_info = rel->pgstat_info;
- if (pgstat_track_counts && pgstat_info != NULL)
+ if (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);
{
PgStat_TableStatus *pgstat_info = rel->pgstat_info;
- if (pgstat_track_counts && pgstat_info != NULL)
+ if (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++;
}
}
{
PgStat_TableStatus *pgstat_info = rel->pgstat_info;
- if (pgstat_track_counts && pgstat_info != NULL)
+ if (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;
+ if (pgstat_info != NULL)
+ 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;
}
static PgBackendStatus *BackendStatusArray = NULL;
static PgBackendStatus *MyBEEntry = NULL;
+static char *BackendClientHostnameBuffer = NULL;
+static char *BackendAppnameBuffer = NULL;
static char *BackendActivityBuffer = NULL;
{
Size size;
- size = add_size(mul_size(sizeof(PgBackendStatus), MaxBackends),
+ size = mul_size(sizeof(PgBackendStatus), MaxBackends);
+ size = add_size(size,
+ mul_size(NAMEDATALEN, MaxBackends));
+ size = add_size(size,
mul_size(pgstat_track_activity_query_size, MaxBackends));
+ size = add_size(size,
+ mul_size(NAMEDATALEN, MaxBackends));
return size;
}
/*
- * Initialize the shared status array and activity string buffer during
- * postmaster startup.
+ * Initialize the shared status array and several string buffers
+ * during postmaster startup.
*/
void
CreateSharedBackendStatus(void)
MemSet(BackendStatusArray, 0, size);
}
+ /* Create or attach to the shared appname buffer */
+ size = mul_size(NAMEDATALEN, MaxBackends);
+ BackendAppnameBuffer = (char *)
+ ShmemInitStruct("Backend Application Name Buffer", size, &found);
+
+ if (!found)
+ {
+ MemSet(BackendAppnameBuffer, 0, size);
+
+ /* Initialize st_appname pointers. */
+ buffer = BackendAppnameBuffer;
+ for (i = 0; i < MaxBackends; i++)
+ {
+ BackendStatusArray[i].st_appname = buffer;
+ buffer += NAMEDATALEN;
+ }
+ }
+
+ /* Create or attach to the shared client hostname buffer */
+ size = mul_size(NAMEDATALEN, MaxBackends);
+ BackendClientHostnameBuffer = (char *)
+ ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
+
+ if (!found)
+ {
+ MemSet(BackendClientHostnameBuffer, 0, size);
+
+ /* Initialize st_clienthostname pointers. */
+ buffer = BackendClientHostnameBuffer;
+ for (i = 0; i < MaxBackends; i++)
+ {
+ BackendStatusArray[i].st_clienthostname = buffer;
+ buffer += NAMEDATALEN;
+ }
+ }
+
/* Create or attach to the shared activity buffer */
size = mul_size(pgstat_track_activity_query_size, MaxBackends);
BackendActivityBuffer = (char *)
* pgstat_bestart() -
*
* Initialize this backend's entry in the PgBackendStatus array.
- * Called from InitPostgres. MyDatabaseId and session userid must be set
+ * Called from InitPostgres.
+ * MyDatabaseId, session userid, and application_name must be set
* (hence, this cannot be combined with pgstat_initialize).
* ----------
*/
beentry->st_databaseid = MyDatabaseId;
beentry->st_userid = userid;
beentry->st_clientaddr = clientaddr;
+ beentry->st_clienthostname[0] = '\0';
beentry->st_waiting = false;
+ beentry->st_appname[0] = '\0';
beentry->st_activity[0] = '\0';
- /* Also make sure the last byte in the string area is always 0 */
+ /* Also make sure the last byte in each string area is always 0 */
+ beentry->st_clienthostname[NAMEDATALEN - 1] = '\0';
+ beentry->st_appname[NAMEDATALEN - 1] = '\0';
beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0';
beentry->st_changecount++;
Assert((beentry->st_changecount & 1) == 0);
+
+ if (MyProcPort && MyProcPort->remote_hostname)
+ strlcpy(beentry->st_clienthostname, MyProcPort->remote_hostname, NAMEDATALEN);
+
+ /* Update app name to current GUC setting */
+ if (application_name)
+ pgstat_report_appname(application_name);
}
/*
volatile PgBackendStatus *beentry = MyBEEntry;
/*
- * If we got as far as discovering our own database ID, we can report
- * what we did to the collector. Otherwise, we'd be sending an invalid
+ * If we got as far as discovering our own database ID, we can report what
+ * we did to the collector. Otherwise, we'd be sending an invalid
* database ID, so forget it. (This means that accesses to pg_database
* during failed backend starts might never get counted.)
*/
Assert((beentry->st_changecount & 1) == 0);
}
+/* ----------
+ * pgstat_report_appname() -
+ *
+ * Called to update our application name.
+ * ----------
+ */
+void
+pgstat_report_appname(const char *appname)
+{
+ volatile PgBackendStatus *beentry = MyBEEntry;
+ int len;
+
+ if (!beentry)
+ return;
+
+ /* This should be unnecessary if GUC did its job, but be safe */
+ len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
+
+ /*
+ * Update my status entry, following the protocol of bumping
+ * st_changecount before and after. We use a volatile pointer here to
+ * ensure the compiler doesn't try to get cute.
+ */
+ beentry->st_changecount++;
+
+ memcpy((char *) beentry->st_appname, appname, len);
+ beentry->st_appname[len] = '\0';
+
+ beentry->st_changecount++;
+ Assert((beentry->st_changecount & 1) == 0);
+}
+
/*
* Report current transaction start timestamp as the specified value.
* Zero means there is no active transaction.
volatile PgBackendStatus *beentry;
PgBackendStatus *localtable;
PgBackendStatus *localentry;
- char *localactivity;
+ char *localappname,
+ *localactivity;
int i;
Assert(!pgStatRunningInCollector);
localtable = (PgBackendStatus *)
MemoryContextAlloc(pgStatLocalContext,
sizeof(PgBackendStatus) * MaxBackends);
+ localappname = (char *)
+ MemoryContextAlloc(pgStatLocalContext,
+ NAMEDATALEN * MaxBackends);
localactivity = (char *)
MemoryContextAlloc(pgStatLocalContext,
pgstat_track_activity_query_size * MaxBackends);
* strcpy is safe even if the string is modified concurrently,
* because there's always a \0 at the end of the buffer.
*/
+ strcpy(localappname, (char *) beentry->st_appname);
+ localentry->st_appname = localappname;
strcpy(localactivity, (char *) beentry->st_activity);
localentry->st_activity = localactivity;
}
if (localentry->st_procpid > 0)
{
localentry++;
+ localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size;
localNumBackends++;
}
{
int rc;
- if (pgStatSock < 0)
+ if (pgStatSock == PGINVALID_SOCKET)
return;
((PgStat_MsgHdr *) msg)->m_size = len;
got_data = (input_fd.revents != 0);
#else /* !HAVE_POLL */
- FD_SET (pgStatSock, &rfds);
+ FD_SET(pgStatSock, &rfds);
/*
* timeout struct is modified by select() on some operating systems,
len);
break;
+ case PGSTAT_MTYPE_RESETSHAREDCOUNTER:
+ pgstat_recv_resetsharedcounter(
+ (PgStat_MsgResetsharedcounter *) &msg,
+ len);
+ break;
+
+ case PGSTAT_MTYPE_RESETSINGLECOUNTER:
+ pgstat_recv_resetsinglecounter(
+ (PgStat_MsgResetsinglecounter *) &msg,
+ len);
+ break;
+
case PGSTAT_MTYPE_AUTOVAC_START:
pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, len);
break;
pgstat_recv_funcpurge((PgStat_MsgFuncpurge *) &msg, len);
break;
+ case PGSTAT_MTYPE_RECOVERYCONFLICT:
+ pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
+ break;
+
default:
break;
}
result->n_tuples_updated = 0;
result->n_tuples_deleted = 0;
result->last_autovac_time = 0;
+ result->n_conflict_tablespace = 0;
+ result->n_conflict_lock = 0;
+ result->n_conflict_snapshot = 0;
+ result->n_conflict_bufferpin = 0;
+ result->n_conflict_startup_deadlock = 0;
+
+ result->stat_reset_timestamp = GetCurrentTimestamp();
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
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;
-
result->vacuum_timestamp = 0;
+ result->vacuum_count = 0;
result->autovac_vacuum_timestamp = 0;
+ result->autovac_vacuum_count = 0;
result->analyze_timestamp = 0;
+ result->analyze_count = 0;
result->autovac_analyze_timestamp = 0;
+ result->autovac_analyze_count = 0;
}
return result;
last_statwrite = globalStats.stats_timestamp;
/*
- * It's not entirely clear whether there could be clock skew between
- * backends and the collector; but just in case someone manages to
- * send us a stats request time that's far in the future, reset it.
- * This ensures that no inquiry message can cause more than one stats
- * file write to occur.
+ * If there is clock skew between backends and the collector, we could
+ * receive a stats request time that's in the future. If so, complain
+ * and reset last_statrequest. Resetting ensures that no inquiry
+ * message can cause more than one stats file write to occur.
*/
- last_statrequest = last_statwrite;
+ if (last_statrequest > last_statwrite)
+ {
+ char *reqtime;
+ char *mytime;
+
+ /* Copy because timestamptz_to_str returns a static buffer */
+ reqtime = pstrdup(timestamptz_to_str(last_statrequest));
+ mytime = pstrdup(timestamptz_to_str(last_statwrite));
+ elog(LOG, "last_statrequest %s is later than collector's time %s",
+ reqtime, mytime);
+ pfree(reqtime);
+ pfree(mytime);
+
+ last_statrequest = last_statwrite;
+ }
}
if (permanent)
*/
memset(&globalStats, 0, sizeof(globalStats));
+ /*
+ * Set the current timestamp (will be kept only in case we can't load an
+ * existing statsfile.
+ */
+ globalStats.stat_reset_timestamp = GetCurrentTimestamp();
+
/*
* Try to open the status file. If it doesn't exist, the backends simply
* return zero for anything and the collector simply starts from scratch
* with empty counters.
+ *
+ * ENOENT is a possibility if the stats collector is not running or has
+ * not yet written the stats file the first time. Any other failure
+ * condition is suspicious.
*/
if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
+ {
+ if (errno != ENOENT)
+ ereport(pgStatRunningInCollector ? LOG : WARNING,
+ (errcode_for_file_access(),
+ errmsg("could not open statistics file \"%s\": %m",
+ statfile)));
return dbhash;
+ }
/*
* Verify it's of the expected format.
|| format_id != PGSTAT_FILE_FORMAT_ID)
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"", statfile)));
goto done;
}
if (fread(&globalStats, 1, sizeof(globalStats), fpin) != sizeof(globalStats))
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"", statfile)));
goto done;
}
fpin) != offsetof(PgStat_StatDBEntry, tables))
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
if (found)
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
fpin) != sizeof(PgStat_StatTabEntry))
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
if (found)
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
fpin) != sizeof(PgStat_StatFuncEntry))
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
if (found)
{
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
default:
ereport(pgStatRunningInCollector ? LOG : WARNING,
- (errmsg("corrupted pgstat.stat file")));
+ (errmsg("corrupted statistics file \"%s\"",
+ statfile)));
goto done;
}
}
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
/*
- * Try to open the status file.
+ * Try to open the status file. As above, anything but ENOENT is worthy
+ * of complaining about.
*/
if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
+ {
+ if (errno != ENOENT)
+ ereport(pgStatRunningInCollector ? LOG : WARNING,
+ (errcode_for_file_access(),
+ errmsg("could not open statistics file \"%s\": %m",
+ statfile)));
return false;
+ }
/*
* 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 statistics file \"%s\"", statfile)));
FreeFile(fpin);
return false;
}
*/
if (fread(&myGlobalStats, 1, sizeof(myGlobalStats), fpin) != sizeof(myGlobalStats))
{
+ ereport(pgStatRunningInCollector ? LOG : WARNING,
+ (errmsg("corrupted statistics file \"%s\"", statfile)));
FreeFile(fpin);
return false;
}
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->vacuum_count = 0;
tabentry->autovac_vacuum_timestamp = 0;
+ tabentry->autovac_vacuum_count = 0;
tabentry->analyze_timestamp = 0;
+ tabentry->analyze_count = 0;
tabentry->autovac_analyze_timestamp = 0;
+ tabentry->autovac_analyze_count = 0;
}
else
{
/*
* 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;
}
}
dbentry->tables = NULL;
dbentry->functions = NULL;
+
+ /*
+ * Reset database-level stats too. This should match the initialization
+ * code in pgstat_get_db_entry().
+ */
dbentry->n_xact_commit = 0;
dbentry->n_xact_rollback = 0;
dbentry->n_blocks_fetched = 0;
dbentry->n_blocks_hit = 0;
+ dbentry->n_tuples_returned = 0;
+ dbentry->n_tuples_fetched = 0;
+ dbentry->n_tuples_inserted = 0;
+ dbentry->n_tuples_updated = 0;
+ dbentry->n_tuples_deleted = 0;
+ dbentry->last_autovac_time = 0;
+
+ dbentry->stat_reset_timestamp = GetCurrentTimestamp();
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
HASH_ELEM | HASH_FUNCTION);
}
+/* ----------
+ * pgstat_recv_resetshared() -
+ *
+ * Reset some shared statistics of the cluster.
+ * ----------
+ */
+static void
+pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
+{
+ if (msg->m_resettarget == RESET_BGWRITER)
+ {
+ /* Reset the global background writer statistics for the cluster. */
+ memset(&globalStats, 0, sizeof(globalStats));
+ globalStats.stat_reset_timestamp = GetCurrentTimestamp();
+ }
+
+ /*
+ * Presumably the sender of this message validated the target, don't
+ * complain here if it's not valid
+ */
+}
+
+/* ----------
+ * pgstat_recv_resetsinglecounter() -
+ *
+ * Reset a statistics for a single object
+ * ----------
+ */
+static void
+pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len)
+{
+ PgStat_StatDBEntry *dbentry;
+
+ dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
+
+ if (!dbentry)
+ return;
+
+ /* Set the reset timestamp for the whole database */
+ dbentry->stat_reset_timestamp = GetCurrentTimestamp();
+
+ /* Remove object if it exists, ignore it if not */
+ if (msg->m_resettype == RESET_TABLE)
+ (void) hash_search(dbentry->tables, (void *) &(msg->m_objectid),
+ HASH_REMOVE, NULL);
+ else if (msg->m_resettype == RESET_FUNCTION)
+ (void) hash_search(dbentry->functions, (void *) &(msg->m_objectid),
+ HASH_REMOVE, NULL);
+}
+
/* ----------
* pgstat_recv_autovac() -
*
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)
- tabentry->n_live_tuples = msg->m_tuples;
+ 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;
+ tabentry->autovac_vacuum_count++;
}
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->vacuum_count++;
}
}
tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
+ 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;
+ tabentry->autovac_analyze_count++;
+ }
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;
+ tabentry->analyze_count++;
+ }
}
globalStats.buf_written_clean += msg->m_buf_written_clean;
globalStats.maxwritten_clean += msg->m_maxwritten_clean;
globalStats.buf_written_backend += msg->m_buf_written_backend;
+ globalStats.buf_fsync_backend += msg->m_buf_fsync_backend;
globalStats.buf_alloc += msg->m_buf_alloc;
}
+/* ----------
+ * pgstat_recv_recoveryconflict() -
+ *
+ * Process as RECOVERYCONFLICT message.
+ * ----------
+ */
+static void
+pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len)
+{
+ PgStat_StatDBEntry *dbentry;
+
+ dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
+
+ switch (msg->m_reason)
+ {
+ case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+
+ /*
+ * Since we drop the information about the database as soon as it
+ * replicates, there is no point in counting these conflicts.
+ */
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ dbentry->n_conflict_tablespace++;
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ dbentry->n_conflict_lock++;
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ dbentry->n_conflict_snapshot++;
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ dbentry->n_conflict_bufferpin++;
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ dbentry->n_conflict_startup_deadlock++;
+ break;
+ }
+}
+
/* ----------
* pgstat_recv_funcstat() -
*