Patch: show xid and xmin in pg_stat_activity and pg_stat_replication
Hi,
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.
This may be helpful when looking for the cause of bloat.
I added two new struct members in PgBackendStatus which get filled in
pgstat_read_current_status() and slightly modified the catalog schema
and the pg_stat_get_activity() procedure.
I'm not sure if it is a good idea to gather the data in
pgstat_read_current_status(), but I chose to do it this way
nonetheless because otherwise I would have to create collector
functions like pgstat_report_xid_assignment() /
pgstat_report_xmin_changed() (accordingly to
pgstat_report_xact_timestamp()) which may result in a performance hit.
Regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 575a40f..f638d2f 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.transactionid,
+ s.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.xmin,
W.state,
W.sent_location,
W.write_location,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index b5ce2f6..3aede64 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"
@@ -2752,6 +2754,8 @@ pgstat_read_current_status(void)
volatile PgBackendStatus *beentry;
PgBackendStatus *localtable;
PgBackendStatus *localentry;
+ PGPROC *proc;
+ PGXACT *xact;
char *localappname,
*localactivity;
int i;
@@ -2815,6 +2819,21 @@ pgstat_read_current_status(void)
/* Only valid entries get included into the local array */
if (localentry->st_procpid > 0)
{
+ proc = BackendIdGetProc(i);
+ /*
+ * should not be necessary, but be conservative
+ */
+ if (proc)
+ {
+ xact = &ProcGlobal->allPgXact[proc->pgprocno];
+
+ if (xact)
+ {
+ localentry->transactionid = xact->xid;
+ localentry->xmin = xact->xmin;
+ }
+ }
+
localentry++;
localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 0533cd6..d1dc7bb 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -534,7 +534,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",
@@ -563,6 +563,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 15, "transactionid",
+ XIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 16, "xmin",
+ XIDOID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@@ -614,8 +618,8 @@ 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;
PgBackendStatus *beentry;
SockAddr zero_clientaddr;
@@ -656,6 +660,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->transactionid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 0117500..ea34d3f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2630,7 +2630,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,transactionid,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,25,25,25,25,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 fb242e4..c329516 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -674,6 +674,16 @@ typedef struct PgBackendStatus
/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
int st_procpid;
+ /* The xid of the current transaction if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId transactionid;
+
+ /* The xmin of the current session if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId xmin;
+
/* Times when current backend, transaction, and activity started */
TimestampTz st_proc_start_timestamp;
TimestampTz st_xact_start_timestamp;
On Tue, Dec 17, 2013 at 9:58 AM, Christian Kruse
<christian@2ndquadrant.com> wrote:
Hi,
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.This may be helpful when looking for the cause of bloat.
I added two new struct members in PgBackendStatus which get filled in
pgstat_read_current_status() and slightly modified the catalog schema
and the pg_stat_get_activity() procedure.I'm not sure if it is a good idea to gather the data in
pgstat_read_current_status(), but I chose to do it this way
nonetheless because otherwise I would have to create collector
functions like pgstat_report_xid_assignment() /
pgstat_report_xmin_changed() (accordingly to
pgstat_report_xact_timestamp()) which may result in a performance hit.
Please add your patch here so we don't lose track of it:
https://commitfest.postgresql.org/action/commitfest_view/open
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 17/12/13 12:08, Robert Haas wrote:
Please add your patch here so we don't lose track of it:
https://commitfest.postgresql.org/action/commitfest_view/open
Thanks. I nearly forgot that.
Regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On 12/17/2013 04:58 PM, Christian Kruse wrote:
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.
Docs.
When an admin is looking for a long-running transaction that's blocking
vacuum, he will currently rely on the timestamp fields, xact_start and
query_start. I'm not sure how much extra value this adds over those
timestamps in pg_stat_activity, but there are not such fields in
pg_stat_replication, so that part is definitely useful. And if we're
going to add xmin to pg_stat_replication, it makes sense to add it to
pg_stat_activity too. Unless someone can come up with something better
to display for walsenders. The timestamp of the last commit record
that's been replayed, perhaps?
What else would a user would want to do with these?
This definitely sounds useful to me as a developer, though. So I'm
thinking we should add these for that reason, in any case.
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 13/01/14 20:06, Heikki Linnakangas wrote:
On 12/17/2013 04:58 PM, Christian Kruse wrote:
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.Docs.
Thanks, update with updated docs is attached.
Regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v2.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 4ec6981..a322d0e 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -619,6 +619,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>transactionid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current transaction identifier.</entry>
+ </row>
+ <row>
+ <entry><structfield>xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current <xref linked="ddl-system-columns">xmin value.</xref></entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1417,6 +1427,11 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current <xref linked="ddl-system-columns">xmin value.</xref></entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 043d118..f00a777 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.transactionid,
+ s.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.xmin,
W.state,
W.sent_location,
W.write_location,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index ffe43ac..18bb1d0 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"
@@ -2762,6 +2764,8 @@ pgstat_read_current_status(void)
volatile PgBackendStatus *beentry;
PgBackendStatus *localtable;
PgBackendStatus *localentry;
+ PGPROC *proc;
+ PGXACT *xact;
char *localappname,
*localactivity;
int i;
@@ -2825,6 +2829,21 @@ pgstat_read_current_status(void)
/* Only valid entries get included into the local array */
if (localentry->st_procpid > 0)
{
+ proc = BackendIdGetProc(i);
+ /*
+ * should not be necessary, but be conservative
+ */
+ if (proc)
+ {
+ xact = &ProcGlobal->allPgXact[proc->pgprocno];
+
+ if (xact)
+ {
+ localentry->transactionid = xact->xid;
+ localentry->xmin = xact->xmin;
+ }
+ }
+
localentry++;
localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 2b8f5ee..e008eda 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -534,7 +534,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",
@@ -563,6 +563,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 15, "transactionid",
+ XIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 16, "xmin",
+ XIDOID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@@ -614,8 +618,8 @@ 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;
PgBackendStatus *beentry;
@@ -655,6 +659,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->transactionid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ab05c46..09d1617 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2630,7 +2630,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,transactionid,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,25,25,25,25,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 96ecc3a..7028c20 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -676,6 +676,16 @@ typedef struct PgBackendStatus
/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
int st_procpid;
+ /* The xid of the current transaction if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId transactionid;
+
+ /* The xmin of the current session if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId xmin;
+
/* Times when current backend, transaction, and activity started */
TimestampTz st_proc_start_timestamp;
TimestampTz st_xact_start_timestamp;
On 14 January 2014 08:38, Christian Kruse <christian@2ndquadrant.com> wrote:
Hi,
On 13/01/14 20:06, Heikki Linnakangas wrote:
On 12/17/2013 04:58 PM, Christian Kruse wrote:
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.Docs.
Thanks, update with updated docs is attached.
Looks simple enough and useful for working out which people are
holding up CONCURRENT activities.
I've not been involved with this patch, so any objections to me doing
final review and commit?
--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jan 29, 2014 at 3:11 PM, Simon Riggs <simon@2ndquadrant.com> wrote:
On 14 January 2014 08:38, Christian Kruse <christian@2ndquadrant.com> wrote:
Hi,
On 13/01/14 20:06, Heikki Linnakangas wrote:On 12/17/2013 04:58 PM, Christian Kruse wrote:
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.Docs.
Thanks, update with updated docs is attached.
Looks simple enough and useful for working out which people are
holding up CONCURRENT activities.I've not been involved with this patch, so any objections to me doing
final review and commit?
Nope, but I think this patch is broken. It looks to me like it's
conflating the process offset in the BackendStatus array with its
backendId, which does not seem like a good idea even if it happens to
work at present. And the way BackendIdGetProc() is used looks unsafe,
too: the contents might no longer be valid by the time we read them.
I suspect we should have a new accessor function that takes a backend
ID and copies the xid and xmin to pointers provided by the client
while holding the lock. I also note that the docs seem to need some
copy-editing:
+ <entry>The current <xref linked="ddl-system-columns">xmin
value.</xref></entry>
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 30 January 2014 17:27, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Jan 29, 2014 at 3:11 PM, Simon Riggs <simon@2ndquadrant.com> wrote:
On 14 January 2014 08:38, Christian Kruse <christian@2ndquadrant.com> wrote:
Hi,
On 13/01/14 20:06, Heikki Linnakangas wrote:On 12/17/2013 04:58 PM, Christian Kruse wrote:
attached you will find a patch for showing the current transaction id
(xid) and the xmin of a backend in pg_stat_activty and the xmin in
pg_stat_replication.Docs.
Thanks, update with updated docs is attached.
Looks simple enough and useful for working out which people are
holding up CONCURRENT activities.I've not been involved with this patch, so any objections to me doing
final review and commit?Nope, but I think this patch is broken. It looks to me like it's
conflating the process offset in the BackendStatus array with its
backendId, which does not seem like a good idea even if it happens to
work at present. And the way BackendIdGetProc() is used looks unsafe,
too: the contents might no longer be valid by the time we read them.
I suspect we should have a new accessor function that takes a backend
ID and copies the xid and xmin to pointers provided by the client
while holding the lock. I also note that the docs seem to need some
copy-editing:+ <entry>The current <xref linked="ddl-system-columns">xmin
value.</xref></entry>
Thanks, saved me the trouble of a detailed review... good catches.
--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-01-30 12:27:43 -0500, Robert Haas wrote:
Nope, but I think this patch is broken. It looks to me like it's
conflating the process offset in the BackendStatus array with its
backendId, which does not seem like a good idea even if it happens to
work at present.
Hm. I don't see how that's going to be broken without major surgery in
pgstat.c. The whole thing seems to rely on being able to index
BackendStatusArray with MyBackendId?
And the way BackendIdGetProc() is used looks unsafe,
too: the contents might no longer be valid by the time we read them.
I suspect we should have a new accessor function that takes a backend
ID and copies the xid and xmin to pointers provided by the client
while holding the lock. I also note that the docs seem to need some
copy-editing:
It certainly needs to be documented as racy, but I don't see a big
problem with being racy here. We assume in lots of places that
writing/reading xids is atomic, and we don't even hold exclusive locks
while writing... (And yes, that means that the xid and xmin don't
necessarily belong to each other)
That said, encapsulating that racy access into a accessor function does
sound like a good plan.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
I suspect we should have a new accessor function that takes a backend
ID and copies the xid and xmin to pointers provided by the client
while holding the lock.
what do you think about the approach the attached patch implements?
I'm not really sure if this is what you had in mind, especially if
this is the right lock.
I also note that the docs seem to need some copy-editing:
+ <entry>The current <xref linked="ddl-system-columns">xmin
value.</xref></entry>
Can you elaborate?
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Fri, Jan 31, 2014 at 4:40 AM, Andres Freund <andres@2ndquadrant.com> wrote:
On 2014-01-30 12:27:43 -0500, Robert Haas wrote:
Nope, but I think this patch is broken. It looks to me like it's
conflating the process offset in the BackendStatus array with its
backendId, which does not seem like a good idea even if it happens to
work at present.Hm. I don't see how that's going to be broken without major surgery in
pgstat.c. The whole thing seems to rely on being able to index
BackendStatusArray with MyBackendId?
Oh, you're right. pgstat_initialize() sets it up that way.
And the way BackendIdGetProc() is used looks unsafe,
too: the contents might no longer be valid by the time we read them.
I suspect we should have a new accessor function that takes a backend
ID and copies the xid and xmin to pointers provided by the client
while holding the lock. I also note that the docs seem to need some
copy-editing:It certainly needs to be documented as racy, but I don't see a big
problem with being racy here. We assume in lots of places that
writing/reading xids is atomic, and we don't even hold exclusive locks
while writing... (And yes, that means that the xid and xmin don't
necessarily belong to each other)
That said, encapsulating that racy access into a accessor function does
sound like a good plan.
Yep, shouldn't be hard to do.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Jan 31, 2014 at 8:02 AM, Christian Kruse
<christian@2ndquadrant.com> wrote:
what do you think about the approach the attached patch implements?
I'm not really sure if this is what you had in mind, especially if
this is the right lock.
The attached patch seems not to be attached, but the right lock to use
would be the same one BackendIdGetProc() is using. I'd add a new
function BackendIdGetTransactionIds or something like that.
I also note that the docs seem to need some copy-editing:
+ <entry>The current <xref linked="ddl-system-columns">xmin
value.</xref></entry>
The link shouldn't include the period, and probably should also not
include the word "value". I would make only the word "xmin" part of
the link.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 31/01/14 11:24, Robert Haas wrote:
what do you think about the approach the attached patch implements?
I'm not really sure if this is what you had in mind, especially if
this is the right lock.The attached patch seems not to be attached, […]
*sighs*
I'm at FOSDEM right now, I will send it as soon as I'm back home.
[…] but the right lock to use would be the same one
BackendIdGetProc() is using. I'd add a new function
BackendIdGetTransactionIds or something like that.
Good – that's exactly what I did (with a slightly different naming).
I also note that the docs seem to need some copy-editing:
+ <entry>The current <xref linked="ddl-system-columns">xmin
value.</xref></entry>The link shouldn't include the period, and probably should also not
include the word "value". I would make only the word "xmin" part of
the link.
Thanks for elaboration.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Hi,
managed to get some time to prepare the patch at FOSDEM. Attached
(this time for real ;-) you will find a new patch version.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v3.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..bef5912 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>transactionid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current transaction identifier.</entry>
+ </row>
+ <row>
+ <entry><structfield>xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current <xref linked="ddl-system-columns">xmin</xref> value.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,11 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current <xref linked="ddl-system-columns">xmin value.</xref></entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f02efec..ded3a04 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.transactionid,
+ s.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.xmin,
W.state,
W.sent_location,
W.write_location,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 305d126..49139d9 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"
@@ -2785,6 +2787,8 @@ pgstat_read_current_status(void)
volatile PgBackendStatus *beentry;
PgBackendStatus *localtable;
PgBackendStatus *localentry;
+ PGPROC *proc;
+ PGXACT *xact;
char *localappname,
*localactivity;
int i;
@@ -2848,6 +2852,8 @@ pgstat_read_current_status(void)
/* Only valid entries get included into the local array */
if (localentry->st_procpid > 0)
{
+ BackendIdGetTransactionIds(localentry->st_procpid, &localentry->transactionid, &localentry->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 e7c3fb2..63243ed 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -401,6 +401,48 @@ BackendIdGetProc(int backendID)
}
/*
+ * BackendIdGetTransactionIds
+ * Get the PGPROC structure for a backend, given the backend ID. Also
+ * 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. NULL is returned if the backend is not active.
+ */
+PGPROC *
+BackendIdGetTransactionIds(int backendPid, TransactionId *xid, TransactionId *xmin)
+{
+ PGPROC *result = NULL;
+ ProcState *stateP;
+ SISeg *segP = shmInvalBuffer;
+ int index = 0;
+ PGXACT *xact;
+
+ /* Need to lock out additions/removals of backends */
+ LWLockAcquire(SInvalWriteLock, LW_SHARED);
+
+ if (backendPid > 0)
+ {
+ for (index = 0; index < segP->lastBackend; index++)
+ {
+ if (segP->procState[index].procPid == backendPid)
+ {
+ stateP = &segP->procState[index];
+ result = stateP->proc;
+ xact = &ProcGlobal->allPgXact[result->pgprocno];
+
+ *xid = xact->xid;
+ *xmin = xact->xmin;
+
+ break;
+ }
+ }
+ }
+
+ LWLockRelease(SInvalWriteLock);
+
+ return result;
+}
+
+/*
* 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 29fc134..62959c1 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, "transactionid",
+ XIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 16, "xmin",
+ XIDOID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@@ -616,8 +620,8 @@ 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;
PgBackendStatus *beentry;
@@ -657,6 +661,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->transactionid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d7bb21e..b2a1b1b 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,transactionid,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,25,25,25,25,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 2024b79..b19c303 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -707,6 +707,16 @@ typedef struct PgBackendStatus
/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
int st_procpid;
+ /* The xid of the current transaction if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId transactionid;
+
+ /* The xmin of the current session if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId xmin;
+
/* Times when current backend, transaction, and activity started */
TimestampTz st_proc_start_timestamp;
TimestampTz st_xact_start_timestamp;
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index 5f2ce48..62e2572 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 PGPROC *BackendIdGetTransactionIds(int backendID, TransactionId *xid, TransactionId *xmin);
extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);
extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize);
Him
On 2014-02-01 17:03:46 +0100, Christian Kruse wrote:
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index a37e6b6..bef5912 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re </entry> </row> <row> + <entry><structfield>transactionid</structfield></entry> + <entry><type>xid</type></entry> + <entry>The current transaction identifier.</entry> + </row>
The other entries refer to the current backend. Maybe
Toplevel transaction identifier of this backend, if any.
+ <row> + <entry><structfield>xmin</structfield></entry> + <entry><type>xid</type></entry> + <entry>The current <xref linked="ddl-system-columns">xmin</xref> value.</entry> + </row> + <row>
I don't think that link is correct, the xmin you're linking to is about
a row's xmin, while the column you're documenting is the backends
current xmin horizon.
Maybe:
The current backend's xmin horizon.
<entry><structfield>query</></entry> <entry><type>text</></entry> <entry>Text of this backend's most recent query. If @@ -1484,6 +1494,11 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re </entry> </row> <row> + <entry><structfield>xmin</structfield></entry> + <entry><type>xid</type></entry> + <entry>The current <xref linked="ddl-system-columns">xmin value.</xref></entry> + </row> + <row>
Wrong link again. This should probably read
"This standby's xmin horizon reported by hot_standby_feedback."
@@ -2785,6 +2787,8 @@ pgstat_read_current_status(void) volatile PgBackendStatus *beentry; PgBackendStatus *localtable; PgBackendStatus *localentry; + PGPROC *proc; + PGXACT *xact;
A bit hard to read from the diff only, but aren't they now unused?
char *localappname, *localactivity; int i; @@ -2848,6 +2852,8 @@ pgstat_read_current_status(void) /* Only valid entries get included into the local array */ if (localentry->st_procpid > 0) { + BackendIdGetTransactionIds(localentry->st_procpid, &localentry->transactionid, &localentry->xmin); +
That's a bit of a long line, try to keep it to 79 chars.
/* + * BackendIdGetTransactionIds + * Get the PGPROC structure for a backend, given the backend ID. Also + * 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. NULL is returned if the backend is not active. + */ +PGPROC * +BackendIdGetTransactionIds(int backendPid, TransactionId *xid, TransactionId *xmin) +{
Hm, why do you even return the PGPROC here? Not that's problematic, but
it seems a bit pointless. If you remove it you can remove a fair bit of
the documentation. I think it should note instead that the two values
can be out of whack with each other.
+ PGPROC *result = NULL; + ProcState *stateP; + SISeg *segP = shmInvalBuffer; + int index = 0; + PGXACT *xact; + + /* Need to lock out additions/removals of backends */ + LWLockAcquire(SInvalWriteLock, LW_SHARED); + + if (backendPid > 0) + { + for (index = 0; index < segP->lastBackend; index++) + { + if (segP->procState[index].procPid == backendPid) + { + stateP = &segP->procState[index]; + result = stateP->proc; + xact = &ProcGlobal->allPgXact[result->pgprocno]; + + *xid = xact->xid; + *xmin = xact->xmin; + + break; + } + } + } + + LWLockRelease(SInvalWriteLock); + + return result; +}
Uh, why do we suddenly need the loop here? BackendIdGetProc() works
without one, so this certainly shouldn't need it, or am I missing
something? Note that Robert withdrew his complaint about relying on
indexing via BackendId in
CA+TgmoaFjcji=Hu9YhCSEL0Z116TY2zEKZf7Z5=da+BPTeytoA@mail.gmail.com .
I also think you need to initialize *xid/*xmin to InvalidTransactionId
if the proc hasn't been found because it exited, otherwise we'll report
stale values.
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi Andres,
ok, lesson learned: don't refactor patches in a rush ;)
[… documation issues …]
Should be fixed according to your critics.
@@ -2785,6 +2787,8 @@ pgstat_read_current_status(void) volatile PgBackendStatus *beentry; PgBackendStatus *localtable; PgBackendStatus *localentry; + PGPROC *proc; + PGXACT *xact;A bit hard to read from the diff only, but aren't they now unused?
Right. Removed.
(This file creates so many warnings with -W -Wall -ansi -pedantic that
I didn't notice that specific warning)
/* + * BackendIdGetTransactionIds + * Get the PGPROC structure for a backend, given the backend ID. Also + * 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. NULL is returned if the backend is not active. + */ +PGPROC * +BackendIdGetTransactionIds(int backendPid, TransactionId *xid, TransactionId *xmin) +{Hm, why do you even return the PGPROC here? Not that's problematic, but
it seems a bit pointless. If you remove it you can remove a fair bit of
the documentation. I think it should note instead that the two values
can be out of whack with each other.
Originally I returned PGPROC to kill to birds with one stone and to be
able to distinguish an error case, e.g. when the backend could not be
found. But in favor of code quality I think your complaint is right
and I should not do that. Instead now xid and xmin are initialized
with InvalidTransactionId.
Uh, why do we suddenly need the loop here? BackendIdGetProc() works
without one, so this certainly shouldn't need it, or am I missing
something? Note that Robert withdrew his complaint about relying on
indexing via BackendId in
CA+TgmoaFjcji=Hu9YhCSEL0Z116TY2zEKZf7Z5=da+BPTeytoA@mail.gmail.com .
Because I was in a rush. Originally I wrote this getter in reaction to
Robert's complaints and refactored it when he took back. But I forgot
to remove the loop, too. Won't happen again, I won't refactor in a
hurry again ;-)
Attached you will find an updated version of the patch.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v4.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..0afd01c 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>transactionid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <link linkend="guc-hot-standby-feedback"><varname>hot_standby_feedback</></link>.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f02efec..ded3a04 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.transactionid,
+ s.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.xmin,
W.state,
W.sent_location,
W.write_location,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 305d126..855cb2e 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"
@@ -2848,6 +2850,10 @@ pgstat_read_current_status(void)
/* Only valid entries get included into the local array */
if (localentry->st_procpid > 0)
{
+ BackendIdGetTransactionIds(localentry->st_procpid,
+ &localentry->transactionid,
+ &localentry->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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 29fc134..62959c1 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, "transactionid",
+ XIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 16, "xmin",
+ XIDOID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@@ -616,8 +620,8 @@ 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;
PgBackendStatus *beentry;
@@ -657,6 +661,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->transactionid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d7bb21e..b2a1b1b 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,transactionid,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,25,25,25,25,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 2024b79..b19c303 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -707,6 +707,16 @@ typedef struct PgBackendStatus
/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
int st_procpid;
+ /* The xid of the current transaction if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId transactionid;
+
+ /* The xmin of the current session if available, InvalidTransactionId
+ * if not
+ */
+ TransactionId xmin;
+
/* Times when current backend, transaction, and activity started */
TimestampTz st_proc_start_timestamp;
TimestampTz st_xact_start_timestamp;
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index 5f2ce48..9b45b3e 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);
On Mon, Feb 3, 2014 at 6:47 AM, Christian Kruse
<christian@2ndquadrant.com> wrote:
[ new patch ]
Is there some compelling reason not to write the documentation link as
<xref linkend="guc-hot-standby-feedback"> rather than using <link>?
It feels weird to me that the new columns are called transactionid and
xmin. Why not xid and xmin?
If I understand correctly, modifying PgBackendStatus adds additional
fields to the shared memory data structure that are never used and
will be returned by functions like pgstat_fetch_stat_beentry()
unitialized. That seems both inefficient and a pitfall for the
unwary.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
It feels weird to me that the new columns are called transactionid and
xmin. Why not xid and xmin?
Actually the part of that that bothers me is "xmin", which conflicts
with a reserved system column name. While you can legally pick such
conflicting names for view columns, it's not going to be so much fun
when you try to join that view against some regular table.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Feb 5, 2014 at 1:21 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
It feels weird to me that the new columns are called transactionid and
xmin. Why not xid and xmin?Actually the part of that that bothers me is "xmin", which conflicts
with a reserved system column name. While you can legally pick such
conflicting names for view columns, it's not going to be so much fun
when you try to join that view against some regular table.
That's a fair point, too. So maybe we should go with something like
backend_xid and backend_xmin or some other prefix that we come up
with. My concern is more that I think they should be consistent
somehow.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Feb 5, 2014 at 1:21 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Actually the part of that that bothers me is "xmin", which conflicts
with a reserved system column name. While you can legally pick such
conflicting names for view columns, it's not going to be so much fun
when you try to join that view against some regular table.
That's a fair point, too. So maybe we should go with something like
backend_xid and backend_xmin or some other prefix that we come up
with. My concern is more that I think they should be consistent
somehow.
Works for me.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-02-05 13:26:15 -0500, Robert Haas wrote:
On Wed, Feb 5, 2014 at 1:21 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
It feels weird to me that the new columns are called transactionid and
xmin. Why not xid and xmin?Actually the part of that that bothers me is "xmin", which conflicts
with a reserved system column name. While you can legally pick such
conflicting names for view columns, it's not going to be so much fun
when you try to join that view against some regular table.That's a fair point, too. So maybe we should go with something like
backend_xid and backend_xmin or some other prefix that we come up
with. My concern is more that I think they should be consistent
somehow.
Those work for me.
We have a bit of a confusing situation atm, pg_prepared_xact calls it's
xid transaction, pg_locks transactionid... So if we add a new speling,
we should like it sufficiently to use it in the future.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
attached you will find a new version with the following issues
resolved:
- use backend ID once again for getting the xid and xmin
- use <xref> instead of <link> in documentation
- rename fields to backend_xid and backend_xmin
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v5.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..1187c9b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f02efec..6cdcf27 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 305d126..c3c4a4c 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"
@@ -2848,6 +2850,10 @@ pgstat_read_current_status(void)
/* Only valid entries get included into the local array */
if (localentry->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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 a4f31cf..5a99cd7 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,8 +620,8 @@ 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;
PgBackendStatus *beentry;
@@ -657,6 +661,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->backend_xid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->backend_xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e6713a6..4238901 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,25,25,25,25,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 5b2e460..a813bc1 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -710,6 +710,16 @@ typedef struct PgBackendStatus
/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
int st_procpid;
+ /* 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;
+
/* Times when current backend, transaction, and activity started */
TimestampTz st_proc_start_timestamp;
TimestampTz st_xact_start_timestamp;
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index 5f2ce48..9b45b3e 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);
On Fri, Feb 7, 2014 at 4:34 AM, Christian Kruse
<christian@2ndquadrant.com> wrote:
attached you will find a new version with the following issues
resolved:- use backend ID once again for getting the xid and xmin
- use <xref> instead of <link> in documentation
- rename fields to backend_xid and backend_xmin
This looks mostly good but it doesn't address this point, from one of
my earlier emails:
If I understand correctly, modifying PgBackendStatus adds additional
fields to the shared memory data structure that are never used and
will be returned by functions like pgstat_fetch_stat_beentry()
unitialized. That seems both inefficient and a pitfall for the
unwary.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-02-11 09:15:45 -0500, Robert Haas wrote:
If I understand correctly, modifying PgBackendStatus adds additional
fields to the shared memory data structure that are never used and
will be returned by functions like pgstat_fetch_stat_beentry()
unitialized. That seems both inefficient and a pitfall for the
unwary.
I don't think the will be unitialized, pgstat_fetch_stat_beentry() will
do a pgstat_read_current_status() if neccessary which will initialize
them.
But they do take up shared memory without really needing to. I
personally don't find that too bad, it's not much memory. If we want to
avoid it we could have a LocalPgBackendStatus that includes the normal
PgBackendStatus. Since pgstat_read_current_status() copies all the data
locally, that'd be a sensible point to fill it. While that will cause a
bit of churn, I'd guess we can use the infrastructure in the not too far
away future for other parts.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On Wednesday 12 February 2014 11:14:56 Andres Freund wrote:
But they do take up shared memory without really needing to. I
personally don't find that too bad, it's not much memory. If we want to
avoid it we could have a LocalPgBackendStatus that includes the normal
PgBackendStatus. Since pgstat_read_current_status() copies all the data
locally, that'd be a sensible point to fill it. While that will cause a
bit of churn, I'd guess we can use the infrastructure in the not too far
away future for other parts.
That's a good idea. Attached you will find a patch implementing it
that way; is this how you pictured it?
Although I'm not sure if this shouldn't be done in two patches, one
for the changes needed for LocalPgBackendStatus and one for the
xid/xmin changes.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v6.patchtext/x-patch; charset=UTF-8; name=xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v6.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..1187c9b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f02efec..6cdcf27 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 305d126..4ab8290 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"
@@ -208,12 +210,13 @@ typedef struct TwoPhasePgStatRecord
bool t_shared; /* is it a shared catalog? */
} TwoPhasePgStatRecord;
+
/*
* Info about current "snapshot" of stats file
*/
static MemoryContext pgStatLocalContext = NULL;
static HTAB *pgStatDBHash = NULL;
-static PgBackendStatus *localBackendStatusTable = NULL;
+static LocalPgBackendStatus *localBackendStatusTable = NULL;
static int localNumBackends = 0;
/*
@@ -2298,7 +2301,7 @@ pgstat_fetch_stat_funcentry(Oid func_id)
* this info (especially the querystring).
* ----------
*/
-PgBackendStatus *
+LocalPgBackendStatus *
pgstat_fetch_stat_beentry(int beid)
{
pgstat_read_current_status();
@@ -2783,8 +2786,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 +2798,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 +2824,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 +2849,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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 a4f31cf..7aaa6ad 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);
@@ -588,11 +592,11 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
for (i = 1; i <= n; i++)
{
- PgBackendStatus *be = pgstat_fetch_stat_beentry(i);
+ LocalPgBackendStatus *be = pgstat_fetch_stat_beentry(i);
if (be)
{
- if (be->st_procpid == pid)
+ if (be->backendStatus.st_procpid == pid)
{
*(int *) (funcctx->user_fctx) = i;
break;
@@ -616,10 +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;
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
MemSet(values, 0, sizeof(values));
MemSet(nulls, 0, sizeof(nulls));
@@ -649,20 +653,26 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
}
/* Values available to all callers */
- values[0] = ObjectIdGetDatum(beentry->st_databaseid);
- values[1] = Int32GetDatum(beentry->st_procpid);
- values[2] = ObjectIdGetDatum(beentry->st_userid);
- if (beentry->st_appname)
- values[3] = CStringGetTextDatum(beentry->st_appname);
+ values[0] = ObjectIdGetDatum(beentry->backendStatus.st_databaseid);
+ values[1] = Int32GetDatum(beentry->backendStatus.st_procpid);
+ values[2] = ObjectIdGetDatum(beentry->backendStatus.st_userid);
+ if (beentry->backendStatus.st_appname)
+ values[3] = CStringGetTextDatum(beentry->backendStatus.st_appname);
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->backend_xid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->backend_xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
- if (superuser() || beentry->st_userid == GetUserId())
+ if (superuser() || beentry->backendStatus.st_userid == GetUserId())
{
SockAddr zero_clientaddr;
- switch (beentry->st_state)
+ switch (beentry->backendStatus.st_state)
{
case STATE_IDLE:
values[4] = CStringGetTextDatum("idle");
@@ -687,32 +697,32 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
break;
}
- values[5] = CStringGetTextDatum(beentry->st_activity);
- values[6] = BoolGetDatum(beentry->st_waiting);
+ values[5] = CStringGetTextDatum(beentry->backendStatus.st_activity);
+ values[6] = BoolGetDatum(beentry->backendStatus.st_waiting);
- if (beentry->st_xact_start_timestamp != 0)
- values[7] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
+ if (beentry->backendStatus.st_xact_start_timestamp != 0)
+ values[7] = TimestampTzGetDatum(beentry->backendStatus.st_xact_start_timestamp);
else
nulls[7] = true;
- if (beentry->st_activity_start_timestamp != 0)
- values[8] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
+ if (beentry->backendStatus.st_activity_start_timestamp != 0)
+ values[8] = TimestampTzGetDatum(beentry->backendStatus.st_activity_start_timestamp);
else
nulls[8] = true;
- if (beentry->st_proc_start_timestamp != 0)
- values[9] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
+ if (beentry->backendStatus.st_proc_start_timestamp != 0)
+ values[9] = TimestampTzGetDatum(beentry->backendStatus.st_proc_start_timestamp);
else
nulls[9] = true;
- if (beentry->st_state_start_timestamp != 0)
- values[10] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
+ if (beentry->backendStatus.st_state_start_timestamp != 0)
+ values[10] = TimestampTzGetDatum(beentry->backendStatus.st_state_start_timestamp);
else
nulls[10] = true;
/* A zeroed client addr means we don't know */
memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
- if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
+ if (memcmp(&(beentry->backendStatus.st_clientaddr), &zero_clientaddr,
sizeof(zero_clientaddr)) == 0)
{
nulls[11] = true;
@@ -721,9 +731,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
}
else
{
- if (beentry->st_clientaddr.addr.ss_family == AF_INET
+ if (beentry->backendStatus.st_clientaddr.addr.ss_family == AF_INET
#ifdef HAVE_IPV6
- || beentry->st_clientaddr.addr.ss_family == AF_INET6
+ || beentry->backendStatus.st_clientaddr.addr.ss_family == AF_INET6
#endif
)
{
@@ -733,18 +743,18 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
remote_host[0] = '\0';
remote_port[0] = '\0';
- ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr,
- beentry->st_clientaddr.salen,
+ ret = pg_getnameinfo_all(&beentry->backendStatus.st_clientaddr.addr,
+ beentry->backendStatus.st_clientaddr.salen,
remote_host, sizeof(remote_host),
remote_port, sizeof(remote_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret == 0)
{
- clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
+ clean_ipv6_addr(beentry->backendStatus.st_clientaddr.addr.ss_family, remote_host);
values[11] = DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host));
- if (beentry->st_clienthostname)
- values[12] = CStringGetTextDatum(beentry->st_clienthostname);
+ if (beentry->backendStatus.st_clienthostname)
+ values[12] = CStringGetTextDatum(beentry->backendStatus.st_clienthostname);
else
nulls[12] = true;
values[13] = Int32GetDatum(atoi(remote_port));
@@ -756,7 +766,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[13] = true;
}
}
- else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX)
+ else if (beentry->backendStatus.st_clientaddr.addr.ss_family == AF_UNIX)
{
/*
* Unix sockets always reports NULL for host and -1 for
@@ -815,12 +825,12 @@ Datum
pg_stat_get_backend_pid(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- PG_RETURN_INT32(beentry->st_procpid);
+ PG_RETURN_INT32(beentry->backendStatus.st_procpid);
}
@@ -828,12 +838,12 @@ Datum
pg_stat_get_backend_dbid(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- PG_RETURN_OID(beentry->st_databaseid);
+ PG_RETURN_OID(beentry->backendStatus.st_databaseid);
}
@@ -841,12 +851,12 @@ Datum
pg_stat_get_backend_userid(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- PG_RETURN_OID(beentry->st_userid);
+ PG_RETURN_OID(beentry->backendStatus.st_userid);
}
@@ -854,17 +864,17 @@ Datum
pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
const char *activity;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
activity = "<backend information not available>";
- else if (!superuser() && beentry->st_userid != GetUserId())
+ else if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
activity = "<insufficient privilege>";
- else if (*(beentry->st_activity) == '\0')
+ else if (*(beentry->backendStatus.st_activity) == '\0')
activity = "<command string not enabled>";
else
- activity = beentry->st_activity;
+ activity = beentry->backendStatus.st_activity;
PG_RETURN_TEXT_P(cstring_to_text(activity));
}
@@ -875,15 +885,15 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
bool result;
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- if (!superuser() && beentry->st_userid != GetUserId())
+ if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
PG_RETURN_NULL();
- result = beentry->st_waiting;
+ result = beentry->backendStatus.st_waiting;
PG_RETURN_BOOL(result);
}
@@ -894,15 +904,15 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
TimestampTz result;
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- if (!superuser() && beentry->st_userid != GetUserId())
+ if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
PG_RETURN_NULL();
- result = beentry->st_activity_start_timestamp;
+ result = beentry->backendStatus.st_activity_start_timestamp;
/*
* No time recorded for start of current query -- this is the case if the
@@ -920,15 +930,15 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
TimestampTz result;
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- if (!superuser() && beentry->st_userid != GetUserId())
+ if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
PG_RETURN_NULL();
- result = beentry->st_xact_start_timestamp;
+ result = beentry->backendStatus.st_xact_start_timestamp;
if (result == 0) /* not in a transaction */
PG_RETURN_NULL();
@@ -942,15 +952,15 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
TimestampTz result;
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- if (!superuser() && beentry->st_userid != GetUserId())
+ if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
PG_RETURN_NULL();
- result = beentry->st_proc_start_timestamp;
+ result = beentry->backendStatus.st_proc_start_timestamp;
if (result == 0) /* probably can't happen? */
PG_RETURN_NULL();
@@ -963,7 +973,7 @@ Datum
pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
SockAddr zero_clientaddr;
char remote_host[NI_MAXHOST];
int ret;
@@ -971,16 +981,16 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- if (!superuser() && beentry->st_userid != GetUserId())
+ if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
PG_RETURN_NULL();
/* A zeroed client addr means we don't know */
memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
- if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
+ if (memcmp(&(beentry->backendStatus.st_clientaddr), &zero_clientaddr,
sizeof(zero_clientaddr)) == 0)
PG_RETURN_NULL();
- switch (beentry->st_clientaddr.addr.ss_family)
+ switch (beentry->backendStatus.st_clientaddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
@@ -992,15 +1002,15 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
}
remote_host[0] = '\0';
- ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr,
- beentry->st_clientaddr.salen,
+ ret = pg_getnameinfo_all(&beentry->backendStatus.st_clientaddr.addr,
+ beentry->backendStatus.st_clientaddr.salen,
remote_host, sizeof(remote_host),
NULL, 0,
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret != 0)
PG_RETURN_NULL();
- clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
+ clean_ipv6_addr(beentry->backendStatus.st_clientaddr.addr.ss_family, remote_host);
PG_RETURN_INET_P(DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host)));
@@ -1010,7 +1020,7 @@ Datum
pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
{
int32 beid = PG_GETARG_INT32(0);
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
SockAddr zero_clientaddr;
char remote_port[NI_MAXSERV];
int ret;
@@ -1018,16 +1028,16 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
PG_RETURN_NULL();
- if (!superuser() && beentry->st_userid != GetUserId())
+ if (!superuser() && beentry->backendStatus.st_userid != GetUserId())
PG_RETURN_NULL();
/* A zeroed client addr means we don't know */
memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
- if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
+ if (memcmp(&(beentry->backendStatus.st_clientaddr), &zero_clientaddr,
sizeof(zero_clientaddr)) == 0)
PG_RETURN_NULL();
- switch (beentry->st_clientaddr.addr.ss_family)
+ switch (beentry->backendStatus.st_clientaddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
@@ -1041,8 +1051,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
}
remote_port[0] = '\0';
- ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr,
- beentry->st_clientaddr.salen,
+ ret = pg_getnameinfo_all(&beentry->backendStatus.st_clientaddr.addr,
+ beentry->backendStatus.st_clientaddr.salen,
NULL, 0,
remote_port, sizeof(remote_port),
NI_NUMERICHOST | NI_NUMERICSERV);
@@ -1065,9 +1075,9 @@ pg_stat_get_db_numbackends(PG_FUNCTION_ARGS)
result = 0;
for (beid = 1; beid <= tot_backends; beid++)
{
- PgBackendStatus *beentry = pgstat_fetch_stat_beentry(beid);
+ LocalPgBackendStatus *beentry = pgstat_fetch_stat_beentry(beid);
- if (beentry && beentry->st_databaseid == dbid)
+ if (beentry && beentry->backendStatus.st_databaseid == dbid)
result++;
}
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e6713a6..4238901 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,25,25,25,25,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 5b2e460..8b418b5 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -735,6 +735,31 @@ 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.
*/
@@ -906,7 +931,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_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 5f2ce48..9b45b3e 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);
On Wed, Feb 12, 2014 at 8:00 AM, Christian Kruse
<christian@2ndquadrant.com> wrote:
On Wednesday 12 February 2014 11:14:56 Andres Freund wrote:
But they do take up shared memory without really needing to. I
personally don't find that too bad, it's not much memory. If we want to
avoid it we could have a LocalPgBackendStatus that includes the normal
PgBackendStatus. Since pgstat_read_current_status() copies all the data
locally, that'd be a sensible point to fill it. While that will cause a
bit of churn, I'd guess we can use the infrastructure in the not too far
away future for other parts.That's a good idea. Attached you will find a patch implementing it
that way; is this how you pictured it?Although I'm not sure if this shouldn't be done in two patches, one
for the changes needed for LocalPgBackendStatus and one for the
xid/xmin changes.
Well, this version of the patch reveals a mighty interesting point: a
lot of the people who are calling pgstat_fetch_stat_beentry() don't
need this additional information and might prefer not to pay the cost
of fetching it. None of pg_stat_get_backend_pid,
pg_stat_get_backend_dbid, pg_stat_get_backend_userid,
pg_stat_get_backend_activity, pg_stat_get_backend_activity,
pg_stat_get_backend_waiting, pg_stat_get_backend_activity_start,
pg_stat_get_backend_xact_start, pg_stat_get_backend_start,
pg_stat_get_backend_client_addr, pg_stat_get_backend_client_port,
pg_stat_get_backend_client_port, and pg_stat_get_db_numbackends
actually need this new information; it's only ever used in one place.
So it seems like it might be wise to have pgstat_fetch_stat_beentry
continue to return the PgBackendStatus * and add a new function
pgstat_fetch_stat_local_beentry to fetch the LocalPgBackendStatus *;
then most of these call sites wouldn't need to change.
It would still be the case that pgstat_read_current_status() pays the
price of fetching this information even if pg_stat_get_activity is
never called. But since that's probably by far the most commonly-used
API for this information, that's probably OK.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-02-14 23:03:43 -0500, Robert Haas wrote:
On Wed, Feb 12, 2014 at 8:00 AM, Christian Kruse
<christian@2ndquadrant.com> wrote:On Wednesday 12 February 2014 11:14:56 Andres Freund wrote:
But they do take up shared memory without really needing to. I
personally don't find that too bad, it's not much memory. If we want to
avoid it we could have a LocalPgBackendStatus that includes the normal
PgBackendStatus. Since pgstat_read_current_status() copies all the data
locally, that'd be a sensible point to fill it. While that will cause a
bit of churn, I'd guess we can use the infrastructure in the not too far
away future for other parts.That's a good idea. Attached you will find a patch implementing it
that way; is this how you pictured it?Although I'm not sure if this shouldn't be done in two patches, one
for the changes needed for LocalPgBackendStatus and one for the
xid/xmin changes.Well, this version of the patch reveals a mighty interesting point: a
lot of the people who are calling pgstat_fetch_stat_beentry() don't
need this additional information and might prefer not to pay the cost
of fetching it. None of [functions]
actually need this new information; it's only ever used in one place.
So it seems like it might be wise to have pgstat_fetch_stat_beentry
continue to return the PgBackendStatus * and add a new function
pgstat_fetch_stat_local_beentry to fetch the LocalPgBackendStatus *;
then most of these call sites wouldn't need to change.
Hm maybe, seems to be as advantageous as not. Less lines changed, but a
essentially duplicate function present.
It would still be the case that pgstat_read_current_status() pays the
price of fetching this information even if pg_stat_get_activity is
never called. But since that's probably by far the most commonly-used
API for this information, that's probably OK.
Agreed.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi Robert,
Am 15.02.14 05:03, schrieb Robert Haas:
Well, this version of the patch reveals a mighty interesting point: a
lot of the people who are calling pgstat_fetch_stat_beentry() don't
need this additional information and might prefer not to pay the cost
of fetching it.
Well, the cost is already paid due to the fact that this patch uses
LocalPgBackendStatus instead of PgBackendStatus in
pgstat_read_current_status(). And pgstat_fetch_stat_beentry() returns
a pointer instead of a copy, so the cost is rather small, too.
None of pg_stat_get_backend_pid,
pg_stat_get_backend_dbid, pg_stat_get_backend_userid,
pg_stat_get_backend_activity, pg_stat_get_backend_activity,
pg_stat_get_backend_waiting, pg_stat_get_backend_activity_start,
pg_stat_get_backend_xact_start, pg_stat_get_backend_start,
pg_stat_get_backend_client_addr, pg_stat_get_backend_client_port,
pg_stat_get_backend_client_port, and pg_stat_get_db_numbackends
actually need this new information; it's only ever used in one place.
So it seems like it might be wise to have pgstat_fetch_stat_beentry
continue to return the PgBackendStatus * and add a new function
pgstat_fetch_stat_local_beentry to fetch the LocalPgBackendStatus *;
then most of these call sites wouldn't need to change.
This is true for now. But one of the purposes of using
LocalPgBackendStatus instead of PgBackendStatus was to be able to add
more fields like this in future. And thus we might need to change this
in future, so why not do it now?
And I also agree to Andres.
It would still be the case that pgstat_read_current_status() pays the
price of fetching this information even if pg_stat_get_activity is
never called. But since that's probably by far the most commonly-used
API for this information, that's probably OK.
I agree.
I will change it if this is really wanted, but I think it would be a
good idea to do it this way.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On 2014-02-17 10:44:41 +0100, Christian Kruse wrote:
This is true for now. But one of the purposes of using
LocalPgBackendStatus instead of PgBackendStatus was to be able to add
more fields like this in future. And thus we might need to change this
in future, so why not do it now?
I don't think that argument is particularly valid. All (?) the other
functions just return just one value, so they won't ever have the need
to return more.
Not really sure which way is better.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 18.02.2014 22:02, Andres Freund wrote:
Not really sure which way is better.
One dev against it, one dev not sure. Enough for me to change it :)
Will post a new patch this evening.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Hi,
attached you will find a new version of the patch with a second getter
function.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v7.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..1187c9b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a7c6a4e..04dfbb0 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 305d126..c8285b8 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;
/*
@@ -2306,6 +2308,28 @@ pgstat_fetch_stat_beentry(int beid)
if (beid < 1 || beid > localNumBackends)
return NULL;
+ return &localBackendStatusTable[beid - 1].backendStatus;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_local_beentry() -
+ *
+ * Like pgstat_fetch_stat_beentry() but with local 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;
+
return &localBackendStatusTable[beid - 1];
}
@@ -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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 a4f31cf..e65b079 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);
@@ -588,11 +592,11 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
for (i = 1; i <= n; i++)
{
- PgBackendStatus *be = pgstat_fetch_stat_beentry(i);
+ LocalPgBackendStatus *be = pgstat_fetch_stat_local_beentry(i);
if (be)
{
- if (be->st_procpid == pid)
+ if (be->backendStatus.st_procpid == pid)
{
*(int *) (funcctx->user_fctx) = i;
break;
@@ -616,10 +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;
- PgBackendStatus *beentry;
+ LocalPgBackendStatus *beentry;
MemSet(values, 0, sizeof(values));
MemSet(nulls, 0, sizeof(nulls));
@@ -627,12 +631,12 @@ 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));
+ beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx));
}
else
{
/* Get the next one in the list */
- beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1); /* 1-based index */
+ beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1); /* 1-based index */
}
if (!beentry)
{
@@ -649,20 +653,26 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
}
/* Values available to all callers */
- values[0] = ObjectIdGetDatum(beentry->st_databaseid);
- values[1] = Int32GetDatum(beentry->st_procpid);
- values[2] = ObjectIdGetDatum(beentry->st_userid);
- if (beentry->st_appname)
- values[3] = CStringGetTextDatum(beentry->st_appname);
+ values[0] = ObjectIdGetDatum(beentry->backendStatus.st_databaseid);
+ values[1] = Int32GetDatum(beentry->backendStatus.st_procpid);
+ values[2] = ObjectIdGetDatum(beentry->backendStatus.st_userid);
+ if (beentry->backendStatus.st_appname)
+ values[3] = CStringGetTextDatum(beentry->backendStatus.st_appname);
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(beentry->backend_xid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(beentry->backend_xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
- if (superuser() || beentry->st_userid == GetUserId())
+ if (superuser() || beentry->backendStatus.st_userid == GetUserId())
{
SockAddr zero_clientaddr;
- switch (beentry->st_state)
+ switch (beentry->backendStatus.st_state)
{
case STATE_IDLE:
values[4] = CStringGetTextDatum("idle");
@@ -687,32 +697,32 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
break;
}
- values[5] = CStringGetTextDatum(beentry->st_activity);
- values[6] = BoolGetDatum(beentry->st_waiting);
+ values[5] = CStringGetTextDatum(beentry->backendStatus.st_activity);
+ values[6] = BoolGetDatum(beentry->backendStatus.st_waiting);
- if (beentry->st_xact_start_timestamp != 0)
- values[7] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
+ if (beentry->backendStatus.st_xact_start_timestamp != 0)
+ values[7] = TimestampTzGetDatum(beentry->backendStatus.st_xact_start_timestamp);
else
nulls[7] = true;
- if (beentry->st_activity_start_timestamp != 0)
- values[8] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
+ if (beentry->backendStatus.st_activity_start_timestamp != 0)
+ values[8] = TimestampTzGetDatum(beentry->backendStatus.st_activity_start_timestamp);
else
nulls[8] = true;
- if (beentry->st_proc_start_timestamp != 0)
- values[9] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
+ if (beentry->backendStatus.st_proc_start_timestamp != 0)
+ values[9] = TimestampTzGetDatum(beentry->backendStatus.st_proc_start_timestamp);
else
nulls[9] = true;
- if (beentry->st_state_start_timestamp != 0)
- values[10] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
+ if (beentry->backendStatus.st_state_start_timestamp != 0)
+ values[10] = TimestampTzGetDatum(beentry->backendStatus.st_state_start_timestamp);
else
nulls[10] = true;
/* A zeroed client addr means we don't know */
memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
- if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
+ if (memcmp(&(beentry->backendStatus.st_clientaddr), &zero_clientaddr,
sizeof(zero_clientaddr)) == 0)
{
nulls[11] = true;
@@ -721,9 +731,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
}
else
{
- if (beentry->st_clientaddr.addr.ss_family == AF_INET
+ if (beentry->backendStatus.st_clientaddr.addr.ss_family == AF_INET
#ifdef HAVE_IPV6
- || beentry->st_clientaddr.addr.ss_family == AF_INET6
+ || beentry->backendStatus.st_clientaddr.addr.ss_family == AF_INET6
#endif
)
{
@@ -733,18 +743,18 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
remote_host[0] = '\0';
remote_port[0] = '\0';
- ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr,
- beentry->st_clientaddr.salen,
+ ret = pg_getnameinfo_all(&beentry->backendStatus.st_clientaddr.addr,
+ beentry->backendStatus.st_clientaddr.salen,
remote_host, sizeof(remote_host),
remote_port, sizeof(remote_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret == 0)
{
- clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
+ clean_ipv6_addr(beentry->backendStatus.st_clientaddr.addr.ss_family, remote_host);
values[11] = DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host));
- if (beentry->st_clienthostname)
- values[12] = CStringGetTextDatum(beentry->st_clienthostname);
+ if (beentry->backendStatus.st_clienthostname)
+ values[12] = CStringGetTextDatum(beentry->backendStatus.st_clienthostname);
else
nulls[12] = true;
values[13] = Int32GetDatum(atoi(remote_port));
@@ -756,7 +766,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[13] = true;
}
}
- else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX)
+ else if (beentry->backendStatus.st_clientaddr.addr.ss_family == AF_UNIX)
{
/*
* Unix sockets always reports NULL for host and -1 for
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 11c1e1a..c001412 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,25,25,25,25,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 5b2e460..e41d991 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -735,6 +735,31 @@ 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 +932,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 5f2ce48..9b45b3e 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);
On 2014-02-21 13:40:59 +0100, Christian Kruse wrote:
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index a4f31cf..e65b079 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);
@@ -588,11 +592,11 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
for (i = 1; i <= n; i++) { - PgBackendStatus *be = pgstat_fetch_stat_beentry(i); + LocalPgBackendStatus *be = pgstat_fetch_stat_local_beentry(i);if (be)
{
- if (be->st_procpid == pid)
+ if (be->backendStatus.st_procpid
== pid)
If we're going this route - which I am ok with - I'd suggest for doing
something like:
- PgBackendStatus *be = pgstat_fetch_stat_beentry(i); + LocalPgBackendStatus *lbe = pgstat_fetch_stat_local_beentry(i); + PgBackendStatus *be = &lb->backendStatus;
There seems little point in making all those lines longer and the
accompanying diff noise if all it costs is a local variable.
Makes sense?
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 21/02/14 13:49, Andres Freund wrote:
If we're going this route - which I am ok with - I'd suggest for doing
something like:- PgBackendStatus *be = pgstat_fetch_stat_beentry(i); + LocalPgBackendStatus *lbe = pgstat_fetch_stat_local_beentry(i); + PgBackendStatus *be = &lb->backendStatus;There seems little point in making all those lines longer and the
accompanying diff noise if all it costs is a local variable.Makes sense?
Makes sense, good idea. Attached you will find a new version of the
patch.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v8.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..1187c9b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a7c6a4e..04dfbb0 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 305d126..c8285b8 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;
/*
@@ -2306,6 +2308,28 @@ pgstat_fetch_stat_beentry(int beid)
if (beid < 1 || beid > localNumBackends)
return NULL;
+ return &localBackendStatusTable[beid - 1].backendStatus;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_local_beentry() -
+ *
+ * Like pgstat_fetch_stat_beentry() but with local 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;
+
return &localBackendStatusTable[beid - 1];
}
@@ -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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 a4f31cf..1f07150 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,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(local_beentry->backend_xid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(local_beentry->backend_xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 11c1e1a..c001412 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,25,25,25,25,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 5b2e460..e41d991 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -735,6 +735,31 @@ 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 +932,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 5f2ce48..9b45b3e 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);
Hi,
and another version with fixed comment style… sorry for traffic :)
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v9.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..1187c9b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a7c6a4e..04dfbb0 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 305d126..c8285b8 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;
/*
@@ -2306,6 +2308,28 @@ pgstat_fetch_stat_beentry(int beid)
if (beid < 1 || beid > localNumBackends)
return NULL;
+ return &localBackendStatusTable[beid - 1].backendStatus;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_local_beentry() -
+ *
+ * Like pgstat_fetch_stat_beentry() but with local 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;
+
return &localBackendStatusTable[beid - 1];
}
@@ -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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 a4f31cf..1f07150 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,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(local_beentry->backend_xid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(local_beentry->backend_xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 11c1e1a..c001412 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,25,25,25,25,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 5b2e460..4daa694 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 5f2ce48..9b45b3e 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);
Hi,
On 2014-02-21 14:15:09 +0100, Christian Kruse wrote:
+/* ---------- + * pgstat_fetch_stat_local_beentry() - + * + * Like pgstat_fetch_stat_beentry() but with local addtions (like xid and + * xmin values of the backend)
s/local addtions/locally computed addititions/
+/* ---------- + * 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; +
Those are sentences, so they should include a . at the end.
I think this is ready for committer.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
attached you will find a fixed version.
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
xid_and_xmin_in_pg_stat_activity_and_pg_stat_replication_v10.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index a37e6b6..1187c9b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a7c6a4e..04dfbb0 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 305d126..06ea588 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;
/*
@@ -2306,6 +2308,28 @@ pgstat_fetch_stat_beentry(int beid)
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;
+
return &localBackendStatusTable[beid - 1];
}
@@ -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 e7c3fb2..e6805d9 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"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * 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 a4f31cf..1f07150 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,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ values[14] = TransactionIdGetDatum(local_beentry->backend_xid);
+ nulls[14] = false;
+
+ values[15] = TransactionIdGetDatum(local_beentry->backend_xmin);
+ nulls[15] = false;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 11c1e1a..c001412 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,25,25,25,25,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 5b2e460..932c83d 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 5f2ce48..9b45b3e 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);
On Mon, Feb 24, 2014 at 2:49 PM, Christian Kruse
<christian@2ndquadrant.com> wrote:
attached you will find a fixed version.
Committed, after fixing the regression test outputs. I also changed
the new fields to be NULL rather than 0 when they are invalid, because
that way applying age() to that column does something useful instead
of something lame.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi Robert,
On 25/02/14 12:37, Robert Haas wrote:
Committed, after fixing the regression test outputs. I also changed
the new fields to be NULL rather than 0 when they are invalid, because
that way applying age() to that column does something useful instead
of something lame.
Hm, thought I had done that. However, thanks for committing and
fixing!
Best regards,
--
Christian Kruse http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services