From dd1a3bccca241a998b4ebf39d67202698e5fa599 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Tue, 25 Feb 2014 12:34:04 -0500 Subject: [PATCH] Show xid and xmin in pg_stat_activity and pg_stat_replication. Christian Kruse, reviewed by Andres Freund and myself, with further minor adjustments by me. --- doc/src/sgml/monitoring.sgml | 16 +++++++++ src/backend/catalog/system_views.sql | 3 ++ src/backend/postmaster/pgstat.c | 50 ++++++++++++++++++++++------ src/backend/storage/ipc/sinvaladt.c | 32 ++++++++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 27 ++++++++++++--- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/pgstat.h | 29 ++++++++++++++++ src/include/storage/sinvaladt.h | 1 + src/test/regress/expected/rules.out | 7 ++-- 10 files changed, 149 insertions(+), 20 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 370857a0e8..958acc4fd1 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -628,6 +628,16 @@ postgres: user database host + + backend_xid + xid + Toplevel transaction identifier of this backend, if any. + + + backend_xmin + xid + The current backend's xmin horizon. + query text @@ -1483,6 +1493,12 @@ postgres: user database host + + backend_xid + xid + This standby's xmin horizon reported + by . + state text diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index a7c6a4e094..04dfbb0ee5 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -586,6 +586,8 @@ CREATE VIEW pg_stat_activity AS S.state_change, S.waiting, S.state, + S.backend_xid, + s.backend_xmin, S.query FROM pg_database D, pg_stat_get_activity(NULL) AS S, pg_authid U WHERE S.datid = D.oid AND @@ -601,6 +603,7 @@ CREATE VIEW pg_stat_replication AS S.client_hostname, S.client_port, S.backend_start, + S.backend_xmin, W.state, W.sent_location, W.write_location, diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 305d126912..06ea588085 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -48,12 +48,14 @@ #include "postmaster/autovacuum.h" #include "postmaster/fork_process.h" #include "postmaster/postmaster.h" +#include "storage/proc.h" #include "storage/backendid.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pg_shmem.h" #include "storage/procsignal.h" +#include "storage/sinvaladt.h" #include "utils/ascii.h" #include "utils/guc.h" #include "utils/memutils.h" @@ -213,7 +215,7 @@ typedef struct TwoPhasePgStatRecord */ static MemoryContext pgStatLocalContext = NULL; static HTAB *pgStatDBHash = NULL; -static PgBackendStatus *localBackendStatusTable = NULL; +static LocalPgBackendStatus *localBackendStatusTable = NULL; static int localNumBackends = 0; /* @@ -2303,6 +2305,28 @@ pgstat_fetch_stat_beentry(int beid) { pgstat_read_current_status(); + if (beid < 1 || beid > localNumBackends) + return NULL; + + return &localBackendStatusTable[beid - 1].backendStatus; +} + + +/* ---------- + * pgstat_fetch_stat_local_beentry() - + * + * Like pgstat_fetch_stat_beentry() but with locally computed addtions (like + * xid and xmin values of the backend) + * + * NB: caller is responsible for a check if the user is permitted to see + * this info (especially the querystring). + * ---------- + */ +LocalPgBackendStatus * +pgstat_fetch_stat_local_beentry(int beid) +{ + pgstat_read_current_status(); + if (beid < 1 || beid > localNumBackends) return NULL; @@ -2783,8 +2807,8 @@ static void pgstat_read_current_status(void) { volatile PgBackendStatus *beentry; - PgBackendStatus *localtable; - PgBackendStatus *localentry; + LocalPgBackendStatus *localtable; + LocalPgBackendStatus *localentry; char *localappname, *localactivity; int i; @@ -2795,9 +2819,9 @@ pgstat_read_current_status(void) pgstat_setup_memcxt(); - localtable = (PgBackendStatus *) + localtable = (LocalPgBackendStatus *) MemoryContextAlloc(pgStatLocalContext, - sizeof(PgBackendStatus) * MaxBackends); + sizeof(LocalPgBackendStatus) * MaxBackends); localappname = (char *) MemoryContextAlloc(pgStatLocalContext, NAMEDATALEN * MaxBackends); @@ -2821,19 +2845,19 @@ pgstat_read_current_status(void) { int save_changecount = beentry->st_changecount; - localentry->st_procpid = beentry->st_procpid; - if (localentry->st_procpid > 0) + localentry->backendStatus.st_procpid = beentry->st_procpid; + if (localentry->backendStatus.st_procpid > 0) { - memcpy(localentry, (char *) beentry, sizeof(PgBackendStatus)); + memcpy(&localentry->backendStatus, (char *) beentry, sizeof(PgBackendStatus)); /* * 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; + localentry->backendStatus.st_appname = localappname; strcpy(localactivity, (char *) beentry->st_activity); - localentry->st_activity = localactivity; + localentry->backendStatus.st_activity = localactivity; } if (save_changecount == beentry->st_changecount && @@ -2846,8 +2870,12 @@ pgstat_read_current_status(void) beentry++; /* Only valid entries get included into the local array */ - if (localentry->st_procpid > 0) + if (localentry->backendStatus.st_procpid > 0) { + BackendIdGetTransactionIds(i, + &localentry->backend_xid, + &localentry->backend_xmin); + localentry++; localappname += NAMEDATALEN; localactivity += pgstat_track_activity_query_size; diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index e7c3fb2f54..e6805d96b1 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -25,6 +25,7 @@ #include "storage/shmem.h" #include "storage/sinvaladt.h" #include "storage/spin.h" +#include "access/transam.h" /* @@ -400,6 +401,37 @@ BackendIdGetProc(int backendID) return result; } +/* + * BackendIdGetTransactionIds + * Get the xid and xmin of the backend. The result may be out of date + * arbitrarily quickly, so the caller must be careful about how this + * information is used. + */ +void +BackendIdGetTransactionIds(int backendID, TransactionId *xid, TransactionId *xmin) +{ + ProcState *stateP; + SISeg *segP = shmInvalBuffer; + PGXACT *xact; + + *xid = InvalidTransactionId; + *xmin = InvalidTransactionId; + + /* Need to lock out additions/removals of backends */ + LWLockAcquire(SInvalWriteLock, LW_SHARED); + + if (backendID > 0 && backendID <= segP->lastBackend) + { + stateP = &segP->procState[backendID - 1]; + xact = &ProcGlobal->allPgXact[stateP->proc->pgprocno]; + + *xid = xact->xid; + *xmin = xact->xmin; + } + + LWLockRelease(SInvalWriteLock); +} + /* * SIInsertDataEntries * Add new invalidation message(s) to the buffer. diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index a4f31cfc7f..140704f97c 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -536,7 +536,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - tupdesc = CreateTemplateTupleDesc(14, false); + tupdesc = CreateTemplateTupleDesc(16, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid", @@ -565,6 +565,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 15, "backend_xid", + XIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin", + XIDOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); @@ -616,9 +620,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) if (funcctx->call_cntr < funcctx->max_calls) { /* for each row */ - Datum values[14]; - bool nulls[14]; + Datum values[16]; + bool nulls[16]; HeapTuple tuple; + LocalPgBackendStatus *local_beentry; PgBackendStatus *beentry; MemSet(values, 0, sizeof(values)); @@ -627,12 +632,14 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) if (*(int *) (funcctx->user_fctx) > 0) { /* Get specific pid slot */ - beentry = pgstat_fetch_stat_beentry(*(int *) (funcctx->user_fctx)); + local_beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx)); + beentry = &local_beentry->backendStatus; } else { /* Get the next one in the list */ - beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1); /* 1-based index */ + local_beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1); /* 1-based index */ + beentry = &local_beentry->backendStatus; } if (!beentry) { @@ -657,6 +664,16 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) else nulls[3] = true; + if (TransactionIdIsValid(local_beentry->backend_xid)) + values[14] = TransactionIdGetDatum(local_beentry->backend_xid); + else + nulls[14] = true; + + if (TransactionIdIsValid(local_beentry->backend_xmin)) + values[15] = TransactionIdGetDatum(local_beentry->backend_xmin); + else + nulls[15] = true; + /* Values only available to same user or superuser */ if (superuser() || beentry->st_userid == GetUserId()) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 68f96bebaf..f8c8f9d5ea 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201402241 +#define CATALOG_VERSION_NO 201402251 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index a2cc19fd7b..e33670ce8e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2632,7 +2632,7 @@ DATA(insert OID = 3057 ( pg_stat_get_autoanalyze_count PGNSP PGUID 12 1 0 0 0 f DESCR("statistics: number of auto analyzes for a table"); DATA(insert OID = 1936 ( pg_stat_get_backend_idset PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ )); DESCR("statistics: currently active backend IDs"); -DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port}" _null_ pg_stat_get_activity _null_ _null_ _null_ )); +DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity _null_ _null_ _null_ )); DESCR("statistics: information about currently active backends"); DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); DESCR("statistics: information about currently active replication"); diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 5b2e4609f6..932c83d79e 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -735,6 +735,34 @@ typedef struct PgBackendStatus char *st_activity; } PgBackendStatus; +/* ---------- + * LocalPgBackendStatus + * + * When we build the backend status array, we use LocalPgBackendStatus to be + * able to add new values to the struct when needed without adding new fields + * to the shared memory. It contains the backend status as a first member. + * ---------- + */ +typedef struct LocalPgBackendStatus +{ + /* + * Local version of the backend status entry. + */ + PgBackendStatus backendStatus; + + /* + * The xid of the current transaction if available, InvalidTransactionId + * if not. + */ + TransactionId backend_xid; + + /* + * The xmin of the current session if available, InvalidTransactionId + * if not. + */ + TransactionId backend_xmin; +} LocalPgBackendStatus; + /* * Working state needed to accumulate per-function-call timing statistics. */ @@ -907,6 +935,7 @@ extern void pgstat_send_bgwriter(void); extern PgStat_StatDBEntry *pgstat_fetch_stat_dbentry(Oid dbid); extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid); extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid); +extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid); extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid); extern int pgstat_fetch_stat_numbackends(void); extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void); diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h index 5f2ce48861..9b45b3efef 100644 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@ -32,6 +32,7 @@ extern Size SInvalShmemSize(void); extern void CreateSharedInvalidationState(void); extern void SharedInvalBackendInit(bool sendOnly); extern PGPROC *BackendIdGetProc(int backendID); +extern void BackendIdGetTransactionIds(int backendID, TransactionId *xid, TransactionId *xmin); extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n); extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 220e18b0bb..ef50f4da21 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1604,9 +1604,11 @@ pg_stat_activity| SELECT s.datid, s.state_change, s.waiting, s.state, + s.backend_xid, + s.backend_xmin, s.query FROM pg_database d, - pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port), + pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid)); pg_stat_all_indexes| SELECT c.oid AS relid, @@ -1704,6 +1706,7 @@ pg_stat_replication| SELECT s.pid, s.client_hostname, s.client_port, s.backend_start, + s.backend_xmin, w.state, w.sent_location, w.write_location, @@ -1711,7 +1714,7 @@ pg_stat_replication| SELECT s.pid, w.replay_location, w.sync_priority, w.sync_state - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port), + FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin), pg_authid u, pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state) WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));